GnuCash  5.6-150-g038405b370+
assistant-qif-import.c
1 /********************************************************************\
2  * assistant-qif-import.c -- window for importing QIF files *
3  * (GnuCash) *
4  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com> *
5  * Copyright (c) 2006 David Hampton <hampton@employees.org> *
6  * Copyright (c) 2011 Robert Fewell *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA gnu@gnu.org *
24 \********************************************************************/
25 
26 #include <config.h>
27 
28 #include <platform.h>
29 #include <libguile.h>
30 #if PLATFORM(WINDOWS)
31 #include <windows.h>
32 #endif
33 
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36 #include <glib/gstdio.h>
37 #include <libguile.h>
38 #include <sys/time.h>
39 #include <unistd.h>
40 
41 #include "Account.h"
42 #include "Transaction.h"
43 #include "dialog-account-picker.h"
44 #include "dialog-commodity.h"
45 #include "dialog-progress.h"
46 #include "dialog-utils.h"
47 #include "dialog-file-access.h"
48 #include "assistant-qif-import.h"
49 #include "gnc-component-manager.h"
50 #include "qof.h"
51 #include "gnc-file.h"
52 #include "gnc-gui-query.h"
53 #include "gnc-guile-utils.h"
54 #include "gnc-currency-edit.h"
55 #include "gnc-ui-util.h"
56 #include "gnc-gtk-utils.h"
57 #include "gnc-main-window.h"
59 #include "gnc-prefs.h"
60 #include "gnc-ui.h"
61 #include "swig-runtime.h"
62 #include "guile-mappings.h"
63 #include <gfec.h>
64 
65 #define ASSISTANT_QIF_IMPORT_CM_CLASS "assistant-qif-import"
66 #define GNC_PREFS_GROUP "dialogs.import.qif"
67 #define GNC_PREF_SHOW_DOC "show-doc"
68 #define GNC_PREF_DEFAULT_TRANS_STATUS_CLEARED "default-status-cleared"
69 #define GNC_PREF_DEFAULT_TRANS_STATUS_NOTCLEARED "default-status-notcleared"
70 #define GNC_PREF_DEFAULT_TRANS_STATUS_RECONCILED "default-status-reconciled"
71 
72 #define PREV_ROW "prev_row"
73 
74 static QofLogModule log_module = GNC_MOD_ASSISTANT;
75 
76 enum filename_cols
77 {
78  FILENAME_COL_INDEX = 0,
79  FILENAME_COL_NAME,
80  NUM_FILENAME_COLS
81 };
82 
83 enum account_cols
84 {
85  ACCOUNT_COL_INDEX = 0,
86  ACCOUNT_COL_QIF_NAME,
87  ACCOUNT_COL_GNC_NAME,
88  ACCOUNT_COL_NEW,
89  ACCOUNT_COL_ELLIPSIZE,
90  NUM_ACCOUNT_COLS
91 };
92 
93 /* to simplify sorting and hence use the default sort function
94  * we store the date as an int64 and convert the gnc_numeric
95  * to a double which can be stored in the liststore.
96  */
97 enum qif_trans_cols
98 {
99  QIF_TRANS_COL_INDEX = 0,
100  QIF_TRANS_COL_DATE,
101  QIF_TRANS_COL_DATE_INT64, // used only for sorting
102  QIF_TRANS_COL_DESCRIPTION,
103  QIF_TRANS_COL_AMOUNT,
104  QIF_TRANS_COL_AMOUNT_DOUBLE, // used only for sorting
105  QIF_TRANS_COL_CHECKED,
106  NUM_QIF_TRANS_COLS
107 };
108 
110 {
111  GtkWidget * window;
112 
113  /* Widgets on the file selection page. */
114  GtkWidget * filename_entry;
115 
116  /* File loading progress page. */
117  GtkWidget * load_pause;
118  GtkWidget * load_start;
119  GtkWidget * load_log;
120  GNCProgressDialog *load_progress;
121 
122  /* Widgets on the default account page. */
123  GtkWidget * acct_entry;
124 
125  /* Widgets on the date format page. */
126  GtkWidget * date_format_combo;
127 
128  /* Widgets on the files loaded page. */
129  GtkWidget * selected_file_view;
130  GtkWidget * unload_file_btn;
131 
132  /* Widgets on the account matching page. */
133  GtkWidget * acct_view;
134  GtkWidget * acct_view_count;
135  GtkWidget * acct_view_btn;
136 
137  /* Widgets on the category matching page. */
138  GtkWidget * cat_view;
139  GtkWidget * cat_view_count;
140  GtkWidget * cat_view_btn;
141 
142  /* Widgets on the memo matching page. */
143  GtkWidget * memo_view;
144  GtkWidget * memo_view_count;
145  GtkWidget * memo_view_btn;
146 
147  /* Widgets on the currency & book options page. */
148  GtkWidget * currency_picker;
149  GtkWidget * book_option_label;
150  GtkWidget * book_option_message;
151 
152  /* Widgets on the commodity page. */
153  gint num_new_pages;
154  GtkWidget * commodity_notebook;
155  GList * commodity_notebook_pages;
156  gint timeout_id;
157 
158  /* Conversion progress page. */
159  GtkWidget * convert_pause;
160  GtkWidget * convert_start;
161  GtkWidget * convert_log;
162  GNCProgressDialog *convert_progress;
163 
164  /* Widgets on the duplicates page. */
165  GtkWidget * new_transaction_view;
166  GtkWidget * old_transaction_view;
167 
168  /* Widgets on the summary page. */
169  GtkWidget * summary_text;
170 
171  gboolean show_doc_pages;
172  gboolean ask_date_format;
173  gboolean busy;
174  gboolean read_file_warnings;
175  gboolean load_stop;
176  gboolean acct_tree_found;
177  gboolean new_book;
178 
179  SCM imported_files;
180  SCM selected_file;
181 
182  SCM acct_map_info;
183  SCM acct_display_info;
184 
185  SCM cat_map_info;
186  SCM cat_display_info;
187 
188  SCM memo_map_info;
189  SCM memo_display_info;
190 
191  SCM gnc_acct_info;
192  SCM security_hash;
193  SCM security_prefs;
194  SCM new_securities;
195  GList * new_namespaces;
196  SCM ticker_map;
197 
198  SCM imported_account_tree;
199  SCM match_transactions;
200  SCM transaction_status;
201  int selected_transaction;
202  gchar *date_format;
203 };
204 
206 {
207  GtkWidget *notebook_page;
208  GtkWidget *namespace_combo;
209  GtkWidget *name_entry;
210  GtkWidget *mnemonic_entry;
211  gnc_commodity *commodity;
212  gboolean page_complete;
213  SCM hash_key;
214 };
215 
216 typedef struct _qifnotebookpage QIFCommNotebookPage;
217 
218 static void gnc_ui_qif_import_assistant_destroy (GtkWidget *object, gpointer user_data);
219 static void gnc_ui_qif_import_assistant_close_handler (gpointer user_data);
220 
221 static gboolean gnc_ui_qif_import_assistant_skip_page (GtkAssistant *assistant, GtkWidget *page, QIFImportWindow *wind);
222 static int gnc_ui_qif_import_assistant_page_forward (int current_page, gpointer data);
223 
224 void gnc_ui_qif_import_cancel_cb (GtkAssistant *gtkassistant, gpointer user_data);
225 void gnc_ui_qif_import_prepare_cb (GtkAssistant *assistant, GtkWidget *page, gpointer user_data);
226 void gnc_ui_qif_import_finish_cb (GtkAssistant *gtkassistant, gpointer user_data);
227 void gnc_ui_qif_import_close_cb (GtkAssistant *gtkassistant, gpointer user_data);
228 
229 void gnc_ui_qif_import_intro_prepare (GtkAssistant *assistant, gpointer user_data);
230 
231 void gnc_ui_qif_import_load_file_prepare (GtkAssistant *assistant, gpointer user_data);
232 void gnc_ui_qif_import_select_file_cb (GtkButton *button, gpointer user_data);
233 
234 void gnc_ui_qif_import_load_progress_prepare (GtkAssistant *assistant, gpointer user_data);
235 void gnc_ui_qif_import_load_progress_pause_cb (GtkButton *button, gpointer user_data);
236 void gnc_ui_qif_import_load_progress_start_cb (GtkButton * button, gpointer user_data);
237 
238 static gboolean gnc_ui_qif_import_skip_date_format (GtkAssistant *assistant, QIFImportWindow *wind);
239 void gnc_ui_qif_import_date_valid_cb (GtkWidget *widget, gpointer user_data);
240 
241 void gnc_ui_qif_import_account_prepare (GtkAssistant *assistant, gpointer user_data);
242 static gboolean gnc_ui_qif_import_skip_account (GtkAssistant *assistant, QIFImportWindow *wind);
243 void gnc_ui_qif_import_acct_valid_cb (GtkWidget *widget, gpointer user_data);
244 void gnc_ui_qif_import_acct_enter_cb (GtkWidget * widget, gpointer user_data);
245 
246 void gnc_ui_qif_import_loaded_files_prepare (GtkAssistant *assistant, gpointer user_data);
247 void gnc_ui_qif_import_load_another_cb (GtkButton *button, gpointer user_data);
248 void gnc_ui_qif_import_unload_file_cb (GtkButton *button, gpointer user_data);
249 
250 static void update_file_page (QIFImportWindow * wind);
251 
252 void gnc_ui_qif_import_account_match_prepare (GtkAssistant *assistant, gpointer user_data);
253 void gnc_ui_qif_import_account_doc_prepare (GtkAssistant *assistant, gpointer user_data);
254 static gboolean gnc_ui_qif_import_skip_account_doc (QIFImportWindow *wind);
255 void gnc_ui_qif_import_account_rematch_cb (GtkButton *button, gpointer user_data);
256 
257 void gnc_ui_qif_import_category_match_prepare (GtkAssistant *assistant, gpointer user_data);
258 static gboolean gnc_ui_qif_import_skip_category_match (QIFImportWindow *wind);
259 void gnc_ui_qif_import_category_doc_prepare (GtkAssistant *assistant, gpointer user_data);
260 static gboolean gnc_ui_qif_import_skip_category_doc (QIFImportWindow *wind);
261 void gnc_ui_qif_import_category_rematch_cb (GtkButton *button, gpointer user_data);
262 
263 void gnc_ui_qif_import_memo_match_prepare (GtkAssistant *assistant, gpointer user_data);
264 static gboolean gnc_ui_qif_import_skip_memo_match (QIFImportWindow *wind);
265 void gnc_ui_qif_import_memo_doc_prepare (GtkAssistant *assistant, gpointer user_data);
266 static gboolean gnc_ui_qif_import_skip_memo_doc (QIFImportWindow *wind);
267 void gnc_ui_qif_import_memo_rematch_cb (GtkButton *button, gpointer user_data);
268 
269 void gnc_ui_qif_import_currency_prepare (GtkAssistant *assistant, gpointer user_data);
270 
271 void gnc_ui_qif_import_commodity_prepare (GtkAssistant *assistant, gpointer user_data);
272 static gboolean gnc_ui_qif_import_skip_commodity (QIFImportWindow *wind);
273 void gnc_ui_qif_import_comm_changed_cb (GtkWidget *widget, gpointer user_data);
274 void gnc_ui_qif_import_comm_namespace_changed_cb (GtkWidget *widget, gpointer user_data);
275 
276 void gnc_ui_qif_import_convert_progress_prepare (GtkAssistant *assistant, gpointer user_data);
277 void gnc_ui_qif_import_convert_progress_pause_cb (GtkButton * button, gpointer user_data);
278 void gnc_ui_qif_import_convert_progress_start_cb (GtkButton * button, gpointer user_data);
279 
280 void gnc_ui_qif_import_duplicates_match_prepare (GtkAssistant *assistant, gpointer user_data);
281 static gboolean gnc_ui_qif_import_skip_duplicates_match (QIFImportWindow *wind);
282 void gnc_ui_qif_import_duplicates_doc_prepare (GtkAssistant *assistant, gpointer user_data);
283 static gboolean gnc_ui_qif_import_skip_duplicates_doc (QIFImportWindow *wind);
284 
285 void gnc_ui_qif_import_end_page_prepare (GtkAssistant *assistant, gpointer user_data);
286 
287 void gnc_ui_qif_import_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
288 
289 static inline void
290 mark_page_complete (GtkAssistant *assistant, gboolean page_status)
291 {
292  gint num = gtk_assistant_get_current_page (assistant);
293  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
294  gtk_assistant_set_page_complete (assistant, page, page_status);
295 }
296 
297 /****************************************************************
298  * update_account_picker_page
299  *
300  * Generic function to update an account_picker page. This
301  * generalizes the code shared whenever any QIF -> GNC mapper is
302  * updating it's LIST STORE. It asks the Scheme side to guess some account
303  * translations and then shows the account name and suggested
304  * translation in the Accounts page view (account picker list).
305  ****************************************************************/
306 static void
307 update_account_picker_page (QIFImportWindow * wind, SCM make_display,
308  GtkWidget *view, SCM map_info, SCM * display_info)
309 {
310  SCM get_qif_name = scm_c_eval_string ("qif-map-entry:qif-name");
311  SCM get_gnc_name = scm_c_eval_string ("qif-map-entry:gnc-name");
312  SCM get_new = scm_c_eval_string ("qif-map-entry:new-acct?");
313  SCM accts_left;
314  gchar *qif_name = NULL;
315  gchar *gnc_name = NULL;
316  gboolean checked;
317  gint row = 0;
318  gint prev_row;
319  GtkListStore *store;
320  GtkTreeIter iter;
321  GtkTreePath *path;
322  GtkTreeSelection *selection;
323 
324  store = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW(view)));
325 
326  /* now get the list of strings to display in the gtk_list_store widget */
327  accts_left = scm_call_3 (make_display,
328  wind->imported_files,
329  map_info,
330  wind->gnc_acct_info);
331 
332  scm_gc_unprotect_object (*display_info);
333  *display_info = accts_left;
334  scm_gc_protect_object (*display_info);
335 
336  /* clear the list */
337  gtk_list_store_clear (store);
338 
339  while (!scm_is_null (accts_left))
340  {
341  qif_name = gnc_scm_call_1_to_string (get_qif_name, SCM_CAR(accts_left));
342  gnc_name = gnc_scm_call_1_to_string (get_gnc_name, SCM_CAR(accts_left));
343  checked = (scm_call_1 (get_new, SCM_CAR(accts_left)) == SCM_BOOL_T);
344 
345  gtk_list_store_append (store, &iter);
346  gtk_list_store_set (store, &iter,
347  ACCOUNT_COL_INDEX, row++,
348  ACCOUNT_COL_QIF_NAME, qif_name,
349  ACCOUNT_COL_GNC_NAME, gnc_name,
350  ACCOUNT_COL_NEW, checked,
351  ACCOUNT_COL_ELLIPSIZE, PANGO_ELLIPSIZE_START,
352  -1);
353  accts_left = SCM_CDR(accts_left);
354  g_free (qif_name);
355  g_free (gnc_name);
356  }
357 
358  /* move to the old selected row */
359  prev_row = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(store), PREV_ROW));
360  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
361 
362  if (prev_row != -1)
363  path = gtk_tree_path_new_from_indices (prev_row, -1);
364  else
365  path = gtk_tree_path_new_from_indices (0, -1);
366 
367  gtk_tree_selection_select_path (selection, path);
368 
369  /* scroll the tree view so the selection is visible if there are rows */
370  if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL(store), NULL) > 0)
371  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), path, NULL, TRUE, 0.5, 0.0);
372  gtk_tree_path_free (path);
373 }
374 
375 
376 /****************************************************************
377  * update_account_page
378  *
379  * update the QIF account -> GNC Account picker
380  ****************************************************************/
381 static void
382 update_account_page (QIFImportWindow * wind)
383 {
384 
385  SCM make_account_display = scm_c_eval_string ("qif-dialog:make-account-display");
386 
387  update_account_picker_page (wind, make_account_display, wind->acct_view,
388  wind->acct_map_info, &(wind->acct_display_info));
389 }
390 
391 
392 /****************************************************************
393  * update_category_page
394  *
395  * update the QIF category -> GNC Account picker
396  ****************************************************************/
397 static void
398 update_category_page (QIFImportWindow * wind)
399 {
400  SCM make_category_display = scm_c_eval_string ("qif-dialog:make-category-display");
401 
402  update_account_picker_page (wind, make_category_display, wind->cat_view,
403  wind->cat_map_info, &(wind->cat_display_info));
404 }
405 
406 
407 /****************************************************************
408  * update_memo_page
409  *
410  * update the QIF memo -> GNC Account picker
411  ****************************************************************/
412 static void
413 update_memo_page (QIFImportWindow * wind)
414 {
415  SCM make_memo_display = scm_c_eval_string ("qif-dialog:make-memo-display");
416 
417  update_account_picker_page (wind, make_memo_display, wind->memo_view,
418  wind->memo_map_info, &(wind->memo_display_info));
419 }
420 
421 
422 /****************************************************************
423  * gnc_ui_qif_import_commodity_destroy
424  *
425  * This function destroys any commodity pages.
426  ****************************************************************/
427 static void
428 gnc_ui_qif_import_commodity_destroy (QIFImportWindow * wind)
429 {
430  GList *pageptr;
431  GtkWidget *notebook_page;
432  QIFCommNotebookPage *comm_nb_page;
433 
434  for (pageptr = wind->commodity_notebook_pages; pageptr; pageptr = pageptr->next)
435  {
436  notebook_page = pageptr->data;
437  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
438 
439  /* Unprotect the Scheme hash key. */
440  scm_gc_unprotect_object (comm_nb_page->hash_key);
441 
442  /* Free the memory allocated for the page's struct. */
443  g_free (comm_nb_page);
444  }
445 
446  /* Free the list of pages. */
447  g_list_free (wind->commodity_notebook_pages);
448  wind->commodity_notebook_pages = NULL;
449 }
450 
451 
452 /**********************************************
453  * gnc_ui_qif_import_assistant_destroy
454  * close the QIF Import assistant window
455  **********************************************/
456 static void
457 gnc_ui_qif_import_assistant_destroy (GtkWidget *object, gpointer user_data)
458 {
459  QIFImportWindow * wind = user_data;
460 
461  /* Destroy the progress dialog helpers. */
462  gnc_progress_dialog_destroy (wind->load_progress);
463 
464  /* Destroy any commodity pages. */
465  gnc_ui_qif_import_commodity_destroy (wind);
466 
467  gnc_unregister_gui_component_by_data (ASSISTANT_QIF_IMPORT_CM_CLASS, wind);
468 
469  gtk_widget_destroy (wind->window);
470 
471  scm_gc_unprotect_object (wind->imported_files);
472  scm_gc_unprotect_object (wind->selected_file);
473  scm_gc_unprotect_object (wind->gnc_acct_info);
474  scm_gc_unprotect_object (wind->cat_display_info);
475  scm_gc_unprotect_object (wind->cat_map_info);
476  scm_gc_unprotect_object (wind->memo_display_info);
477  scm_gc_unprotect_object (wind->memo_map_info);
478  scm_gc_unprotect_object (wind->acct_display_info);
479  scm_gc_unprotect_object (wind->acct_map_info);
480  scm_gc_unprotect_object (wind->security_hash);
481  scm_gc_unprotect_object (wind->security_prefs);
482  scm_gc_unprotect_object (wind->new_securities);
483  scm_gc_unprotect_object (wind->ticker_map);
484  scm_gc_unprotect_object (wind->imported_account_tree);
485  scm_gc_unprotect_object (wind->match_transactions);
486 
487  g_free (wind);
488 }
489 
490 
491 /****************************************************************
492  * gnc_ui_qif_import_select_loaded_file_cb
493  * callback when a file is clicked in the "loaded files" page
494  ****************************************************************/
495 static void
496 gnc_ui_qif_import_select_loaded_file_cb (GtkTreeSelection *selection,
497  gpointer user_data)
498 {
499  QIFImportWindow * wind = user_data;
500  GtkTreeModel *model;
501  GtkTreeIter iter;
502  gint row;
503  GtkWidget *button;
504 
505  button = (wind->unload_file_btn);
506  if (gtk_tree_selection_get_selected (selection, &model, &iter))
507  {
508  gtk_tree_model_get (model, &iter, FILENAME_COL_INDEX, &row, -1);
509  if (scm_is_list (wind->imported_files) &&
510  (scm_ilength (wind->imported_files) > row))
511  {
512  scm_gc_unprotect_object (wind->selected_file);
513  wind->selected_file = scm_list_ref (wind->imported_files,
514  scm_from_int (row));
515  scm_gc_protect_object (wind->selected_file);
516  g_object_set (button, "sensitive", TRUE, (gchar*)NULL);
517  }
518  }
519  else
520  {
521  scm_gc_unprotect_object (wind->selected_file);
522  wind->selected_file = SCM_BOOL_F;
523  scm_gc_protect_object (wind->selected_file);
524  g_object_set (button, "sensitive", FALSE, (gchar*)NULL);
525  }
526 }
527 
528 
529 /****************************************************
530  * create_account_picker_view
531  ****************************************************/
532 static void
533 create_account_picker_view (GtkWidget *widget,
534  const gchar *col_name,
535  GCallback activate_cb,
536  GCallback select_cb,
537  gpointer user_data)
538 {
539  GtkTreeView *view = GTK_TREE_VIEW(widget);
540  GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
541  GtkListStore *store;
542  GtkCellRenderer *renderer;
543  GtkTreeViewColumn *column;
544 
545  store = gtk_list_store_new (NUM_ACCOUNT_COLS, G_TYPE_INT, G_TYPE_STRING,
546  G_TYPE_STRING, G_TYPE_BOOLEAN,
547  PANGO_TYPE_ELLIPSIZE_MODE);
548  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
549 
550  /* prevent the rows being dragged to a different order */
551  gtk_tree_view_set_reorderable (view, FALSE);
552 
553  /* default sort order */
554  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
555  ACCOUNT_COL_QIF_NAME,
556  GTK_SORT_ASCENDING);
557  g_object_unref (store);
558 
559  renderer = gtk_cell_renderer_text_new ();
560  column = gtk_tree_view_column_new_with_attributes (col_name,
561  renderer,
562  "text",
563  ACCOUNT_COL_QIF_NAME,
564  "ellipsize",
565  ACCOUNT_COL_ELLIPSIZE,
566  NULL);
567 
568  g_object_set (G_OBJECT(column), "expand", TRUE, "reorderable",
569  TRUE, "resizable", TRUE, NULL);
570 
571  gtk_tree_view_append_column (view, column);
572  gtk_tree_view_column_set_sort_column_id (column, ACCOUNT_COL_QIF_NAME);
573 
574  renderer = gtk_cell_renderer_text_new ();
575  column = gtk_tree_view_column_new_with_attributes (_("GnuCash account name"),
576  renderer,
577  "text",
578  ACCOUNT_COL_GNC_NAME,
579  "ellipsize",
580  ACCOUNT_COL_ELLIPSIZE,
581  NULL);
582 
583  g_object_set (G_OBJECT(column), "expand", TRUE, "reorderable",
584  TRUE, "resizable", TRUE, NULL);
585 
586  gtk_tree_view_append_column (view, column);
587  gtk_tree_view_column_set_sort_column_id (column, ACCOUNT_COL_GNC_NAME);
588 
589  renderer = gtk_cell_renderer_toggle_new ();
590  g_object_set(renderer, "activatable", FALSE, NULL);
591  column = gtk_tree_view_column_new_with_attributes (_("New?"),
592  renderer,
593  "active",
594  ACCOUNT_COL_NEW,
595  NULL);
596  gtk_tree_view_append_column (view, column);
597 
598  g_object_set_data (G_OBJECT(store), PREV_ROW, GINT_TO_POINTER(-1));
599 
600  /* Connect the signal handlers. */
601  g_signal_connect (view, "row-activated", G_CALLBACK(activate_cb), user_data);
602  g_signal_connect (selection, "changed", G_CALLBACK(select_cb), user_data);
603 
604  /* Allow multiple rows to be selected. */
605  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
606 }
607 
608 
609 /********************************************************************
610  * rematch_line
611  *
612  * This is a helper function for tree controls used by some assistant
613  * pages for mapping QIF values to GnuCash accounts. It processes
614  * the selected rows when a user tries to edit the account mappings.
615  * The account picker is displayed, and the chosen GnuCash account
616  * becomes the new mapping for each row. Finally, the update_page
617  * function is called.
618  ********************************************************************/
619 static void
620 rematch_line (QIFImportWindow *wind, GtkTreeSelection *selection,
621  SCM display_info, SCM map_info,
622  void (*update_page)(QIFImportWindow *))
623 {
624  SCM get_qif_name = scm_c_eval_string ("qif-map-entry:qif-name");
625  SCM get_gnc_name = scm_c_eval_string ("qif-map-entry:gnc-name");
626  SCM set_gnc_name = scm_c_eval_string ("qif-map-entry:set-gnc-name!");
627  SCM map_entry;
628  SCM gnc_name;
629  GList *pathlist;
630  GList *current;
631  GtkTreeModel *model;
632  GtkTreeIter iter;
633  gint row;
634 
635  /* Get a list of selected rows. */
636  pathlist = gtk_tree_selection_get_selected_rows (selection, &model);
637  if (!pathlist)
638  return;
639 
640  /*
641  * Update the first selected row.
642  */
643 
644  /* Get the row number of the first selected row. */
645  if (!gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) pathlist->data))
646  return;
647  gtk_tree_model_get (model, &iter, ACCOUNT_COL_INDEX, &row, -1);
648 
649  /* Save the row number. */
650  g_object_set_data (G_OBJECT(model), PREV_ROW, GINT_TO_POINTER(row));
651  if (row == -1)
652  return;
653 
654  /* Find the <qif-map-entry> corresponding to the selected row. */
655  map_entry = scm_list_ref (display_info, scm_from_int (row));
656 
657  /* Call the account picker to update it. */
658  if (!qif_account_picker_dialog (GTK_WINDOW(wind->window), wind, map_entry))
659  return;
660  gnc_name = scm_call_1 (get_gnc_name, map_entry);
661 
662  /* Update the mapping hash table. */
663  scm_hash_set_x (map_info, scm_call_1 (get_qif_name, map_entry), map_entry);
664 
665  /*
666  * Map all other selected rows to the same GnuCash account.
667  */
668  for (current = pathlist->next; current; current = current->next)
669  {
670  /* Get the row number. */
671  gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) current->data);
672  gtk_tree_model_get (model, &iter, ACCOUNT_COL_INDEX, &row, -1);
673 
674  /* Update the <qif-map-entry> for the selected row. */
675  map_entry = scm_list_ref (display_info, scm_from_int (row));
676  scm_call_2 (set_gnc_name, map_entry, gnc_name);
677 
678  /* Update the mapping hash table. */
679  scm_hash_set_x (map_info, scm_call_1 (get_qif_name, map_entry), map_entry);
680  }
681 
682  /* Free the path list. */
683  g_list_foreach (pathlist, (GFunc) gtk_tree_path_free, NULL);
684  g_list_free (pathlist);
685 
686  /* Update the display. */
687  update_page (wind);
688 }
689 
690 
691 /********************************************************************
692  * gnc_ui_qif_import_account_activate_cb
693  *
694  * This handler is invoked when a row is double-clicked in the "Match
695  * QIF accounts to GnuCash accounts" page.
696  ********************************************************************/
697 static void
698 gnc_ui_qif_import_account_activate_cb (GtkTreeView *view, GtkTreePath *path,
699  GtkTreeViewColumn *column,
700  gpointer user_data)
701 {
702  QIFImportWindow *wind = user_data;
703 
704  g_return_if_fail (wind);
705 
706  rematch_line (wind, gtk_tree_view_get_selection (view),
707  wind->acct_display_info, wind->acct_map_info,
708  update_account_page);
709 }
710 
711 
712 /********************************************************************
713  * gnc_ui_qif_import_account_select_cb
714  *
715  * This handler is invoked when the selection of account matchings
716  * has changed. It updates the selection count and enables/disables
717  * the "Change" button.
718  ********************************************************************/
719 static void
720 gnc_ui_qif_import_account_select_cb (GtkTreeSelection *selection,
721  gpointer user_data)
722 {
723  QIFImportWindow *wind = user_data;
724  gint count = gtk_tree_selection_count_selected_rows (selection);
725  gchar *count_str;
726 
727  g_return_if_fail (wind);
728 
729  /* Update the "items selected" count. */
730  if (wind->acct_view_count)
731  {
732  count_str = g_strdup_printf ("%d", count);
733  gtk_label_set_text (GTK_LABEL(wind->acct_view_count), count_str);
734  g_free (count_str);
735  }
736 
737  /* Enable/disable the Change button. */
738  if (wind->acct_view_btn)
739  {
740  if (count)
741  gtk_widget_set_sensitive (wind->acct_view_btn, TRUE);
742  else
743  gtk_widget_set_sensitive (wind->acct_view_btn, FALSE);
744  }
745 }
746 
747 
748 /********************************************************************
749  * gnc_ui_qif_import_category_activate_cb
750  *
751  * This handler is invoked when a row is double-clicked in the "Match
752  * QIF categories to GnuCash accounts" page.
753  ********************************************************************/
754 static void
755 gnc_ui_qif_import_category_activate_cb (GtkTreeView *view, GtkTreePath *path,
756  GtkTreeViewColumn *column,
757  gpointer user_data)
758 {
759  QIFImportWindow *wind = user_data;
760  GtkTreeSelection *selection;
761 
762  g_return_if_fail (view && wind);
763  selection = gtk_tree_view_get_selection (view);
764 
765  rematch_line (wind, selection, wind->cat_display_info, wind->cat_map_info,
766  update_category_page);
767 }
768 
769 
770 /********************************************************************
771  * gnc_ui_qif_import_category_select_cb
772  *
773  * This handler is invoked when the selection of category matchings
774  * has changed. It updates the selection count and enables/disables
775  * the "Change" button.
776  ********************************************************************/
777 static void
778 gnc_ui_qif_import_category_select_cb (GtkTreeSelection *selection,
779  gpointer user_data)
780 {
781  QIFImportWindow *wind = user_data;
782  gint count = gtk_tree_selection_count_selected_rows (selection);
783  gchar *count_str;
784 
785  g_return_if_fail (wind);
786 
787  /* Update the "items selected" count. */
788  if (wind->cat_view_count)
789  {
790  count_str = g_strdup_printf ("%d", count);
791  gtk_label_set_text (GTK_LABEL(wind->cat_view_count), count_str);
792  g_free (count_str);
793  }
794 
795  /* Enable/disable the Change button. */
796  if (wind->cat_view_btn)
797  {
798  if (count)
799  gtk_widget_set_sensitive (wind->cat_view_btn, TRUE);
800  else
801  gtk_widget_set_sensitive (wind->cat_view_btn, FALSE);
802  }
803 }
804 
805 
806 /********************************************************************
807  * gnc_ui_qif_import_memo_activate_cb
808  *
809  * This handler is invoked when a row is double-clicked in the "Match
810  * QIF payee/memo to GnuCash accounts" page.
811  ********************************************************************/
812 static void
813 gnc_ui_qif_import_memo_activate_cb (GtkTreeView *view, GtkTreePath *path,
814  GtkTreeViewColumn *column,
815  gpointer user_data)
816 {
817  QIFImportWindow *wind = user_data;
818  GtkTreeSelection *selection;
819 
820  g_return_if_fail (view && wind);
821  selection = gtk_tree_view_get_selection (view);
822 
823  rematch_line (wind, selection, wind->memo_display_info, wind->memo_map_info,
824  update_memo_page);
825 }
826 
827 
828 /********************************************************************
829  * gnc_ui_qif_import_memo_select_cb
830  *
831  * This handler is invoked when the selection of memo matchings
832  * has changed. It updates the selection count and enables/disables
833  * the "Change" button.
834  ********************************************************************/
835 static void
836 gnc_ui_qif_import_memo_select_cb (GtkTreeSelection *selection,
837  gpointer user_data)
838 {
839  QIFImportWindow *wind = user_data;
840  gint count = gtk_tree_selection_count_selected_rows (selection);
841  gchar *count_str;
842 
843  g_return_if_fail (wind);
844 
845  /* Update the "items selected" count. */
846  if (wind->memo_view_count)
847  {
848  count_str = g_strdup_printf ("%d", count);
849  gtk_label_set_text (GTK_LABEL(wind->memo_view_count), count_str);
850  g_free (count_str);
851  }
852 
853  /* Enable/disable the Change button. */
854  if (wind->memo_view_btn)
855  {
856  if (count)
857  gtk_widget_set_sensitive (wind->memo_view_btn, TRUE);
858  else
859  gtk_widget_set_sensitive (wind->memo_view_btn, FALSE);
860  }
861 }
862 
863 
864 /*********************************************
865  * new_security_notebook_page
866  *********************************************/
867 static QIFCommNotebookPage *
868 new_security_notebook_page (SCM security_hash_key, gnc_commodity *comm, QIFImportWindow *wind)
869 {
870  QIFCommNotebookPage *comm_nb_page = g_new0(QIFCommNotebookPage, 1);
871  GtkListStore *store;
872  GtkWidget *table;
873  GtkWidget *label;
874  gchar *title = NULL;
875  const char *str;
876  GtkWidget *notebook_page;
877  GtkWidget *notebook_label;
878  GtkWidget *entry;
879  char *name_tooltip =
880  _("Enter a name or short description, such as \"Red Hat Stock\".");
881  char *mnemonic_tooltip =
882  _("Enter the ticker symbol or other well known abbreviation, such as"
883  " \"RHT\". If there isn't one, or you don't know it, create your own.");
884  char *namespace_tooltip =
885  _("Select the exchange on which the symbol is traded, or select the"
886  " type of investment (such as FUND for mutual funds.) If you don't"
887  " see your exchange or an appropriate investment type, you can"
888  " enter a new one.");
889 
890  /* Make the page widget. */
891  notebook_page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
892  gtk_box_set_homogeneous (GTK_BOX (notebook_page), FALSE);
893  comm_nb_page->notebook_page = notebook_page;
894  g_object_set_data (G_OBJECT(notebook_page), "page_struct", comm_nb_page);
895 
896  /* Save the commodity and the hash table key. */
897  comm_nb_page->commodity = comm;
898  comm_nb_page->hash_key = security_hash_key;
899  scm_gc_protect_object (comm_nb_page->hash_key);
900 
901  /* Set the page title. */
902  str = gnc_commodity_get_mnemonic (comm);
903  str = str ? str : "";
904  title = g_strdup_printf ("\"%s\"", str);
905 
906  /* Insert the new notebook page */
907  notebook_label = gtk_label_new (title);
908  gnc_label_set_alignment (notebook_label, 0.0, 0.5);
909  gtk_notebook_append_page (GTK_NOTEBOOK(wind->commodity_notebook),
910  notebook_page, notebook_label);
911  g_free (title);
912 
913  /* set the page complete flag as on creation all fields will be OK */
914  comm_nb_page->page_complete = TRUE;
915 
916  /* Add all the widgets to the page. */
917  table = gtk_grid_new ();
918  gtk_grid_set_row_spacing (GTK_GRID(table), 6);
919  gtk_grid_set_column_spacing (GTK_GRID(table), 12);
920 
921  /* Name entry */
922  comm_nb_page->name_entry = gtk_entry_new ();
923  gtk_entry_set_text (GTK_ENTRY(comm_nb_page->name_entry),
925  label = gtk_label_new_with_mnemonic (_("Name or _description"));
926  gtk_label_set_mnemonic_widget (GTK_LABEL(label), comm_nb_page->name_entry);
927  gnc_label_set_alignment (label, 0, 0.5);
928 
929  gtk_widget_set_tooltip_text (label, name_tooltip);
930  gtk_widget_set_tooltip_text (comm_nb_page->name_entry, name_tooltip);
931 
932  gtk_grid_attach (GTK_GRID(table), label, 0, 0, 1, 1);
933  gtk_widget_set_halign (label, GTK_ALIGN_FILL);
934  gtk_widget_set_valign (label, GTK_ALIGN_FILL);
935  gtk_widget_set_hexpand (label, TRUE);
936  gtk_widget_set_vexpand (label, FALSE);
937  g_object_set (label, "margin", 0, NULL);
938 
939  gtk_grid_attach (GTK_GRID(table), comm_nb_page->name_entry, 1, 0, 1, 1);
940 
941  g_signal_connect (comm_nb_page->name_entry, "changed",
942  G_CALLBACK(gnc_ui_qif_import_comm_changed_cb), wind);
943 
944  /* Mnemonic entry */
945  comm_nb_page->mnemonic_entry = gtk_entry_new ();
946  gtk_entry_set_text (GTK_ENTRY(comm_nb_page->mnemonic_entry),
948  label = gtk_label_new_with_mnemonic (
949  _("_Ticker symbol or other abbreviation"));
950  gtk_label_set_mnemonic_widget (GTK_LABEL(label), comm_nb_page->mnemonic_entry);
951  gnc_label_set_alignment (label, 0, 0.5);
952 
953  gtk_widget_set_tooltip_text (label, mnemonic_tooltip);
954  gtk_widget_set_tooltip_text (comm_nb_page->mnemonic_entry, mnemonic_tooltip);
955 
956  gtk_grid_attach (GTK_GRID(table), label, 0, 1, 1, 1);
957  gtk_widget_set_halign (label, GTK_ALIGN_FILL);
958  gtk_widget_set_valign (label, GTK_ALIGN_FILL);
959  gtk_widget_set_hexpand (label, TRUE);
960  gtk_widget_set_vexpand (label, FALSE);
961  g_object_set (label, "margin", 0, NULL);
962 
963  gtk_grid_attach (GTK_GRID(table), comm_nb_page->mnemonic_entry, 1, 1, 1, 1);
964 
965  g_signal_connect (comm_nb_page->mnemonic_entry, "changed",
966  G_CALLBACK(gnc_ui_qif_import_comm_changed_cb), wind);
967 
968  /* Namespace entry */
969  store = gtk_list_store_new (1, G_TYPE_STRING);
970  comm_nb_page->namespace_combo = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(store));
971  g_object_unref (store);
972 
973  entry = gtk_bin_get_child (GTK_BIN(comm_nb_page->namespace_combo));
974  gtk_widget_set_events (GTK_WIDGET(entry), GDK_FOCUS_CHANGE_MASK);
975  g_signal_connect (G_OBJECT (entry), "changed",
976  G_CALLBACK(gnc_ui_qif_import_comm_namespace_changed_cb), wind);
977 
978  /* Set the column for the text */
979  gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(comm_nb_page->namespace_combo), 0);
980 
981  gnc_cbwe_add_completion (GTK_COMBO_BOX(comm_nb_page->namespace_combo));
982  label = gtk_label_new_with_mnemonic (
983  _("_Exchange or abbreviation type"));
984  gtk_label_set_mnemonic_widget (GTK_LABEL(label), comm_nb_page->namespace_combo);
985  gnc_label_set_alignment (label, 0, 0.5);
986 
987  gtk_widget_set_tooltip_text (label, namespace_tooltip);
988  gtk_widget_set_tooltip_text (comm_nb_page->namespace_combo, namespace_tooltip);
989 
990  gtk_grid_attach (GTK_GRID(table), label, 0, 2, 1, 1);
991  gtk_widget_set_halign (label, GTK_ALIGN_FILL);
992  gtk_widget_set_valign (label, GTK_ALIGN_FILL);
993  gtk_widget_set_hexpand (label, TRUE);
994  gtk_widget_set_vexpand (label, FALSE);
995  g_object_set (label, "margin", 0, NULL);
996 
997  gtk_grid_attach (GTK_GRID(table), comm_nb_page->namespace_combo, 1, 2, 1, 1);
998  gtk_container_set_border_width (GTK_CONTAINER(notebook_page), 12);
999  gtk_box_pack_start (GTK_BOX(notebook_page), table, FALSE, FALSE, 12);
1000  gtk_widget_show_all (GTK_WIDGET(wind->commodity_notebook));
1001  return comm_nb_page;
1002 }
1003 
1004 
1005 /********************************************************************
1006  * prepare_security_pages
1007  *
1008  * Prepare the assistant page for each security.
1009  ********************************************************************/
1010 static void
1011 prepare_security_pages (QIFImportWindow * wind)
1012 {
1013  SCM hash_ref = scm_c_eval_string ("hash-ref");
1014  SCM securities;
1015  SCM comm_ptr_token;
1016 
1017  GList * current;
1018  gnc_commodity * commodity;
1019  QIFCommNotebookPage * new_comm_nb_page;
1020 
1021  /*
1022  * Make assistant pages for each new QIF security.
1023  */
1024  gnc_set_busy_cursor (NULL, TRUE);
1025  securities = wind->new_securities;
1026  current = wind->commodity_notebook_pages;
1027  while (!scm_is_null (securities) && (securities != SCM_BOOL_F))
1028  {
1029  if (current)
1030  {
1031  /* The page has already been made. */
1032  current = current->next;
1033  }
1034  else
1035  {
1036  /* Get the GnuCash commodity corresponding to the new QIF security. */
1037  comm_ptr_token = scm_call_2 (hash_ref,
1038  wind->security_hash,
1039  SCM_CAR(securities));
1040 
1041 #define FUNC_NAME "new_security_notebook_page"
1042  commodity = SWIG_MustGetPtr (comm_ptr_token,
1043  SWIG_TypeQuery ("_p_gnc_commodity"), 1, 0);
1044 #undef FUNC_NAME
1045 
1046  /* Build a new security notebook page. */
1047  new_comm_nb_page = new_security_notebook_page (SCM_CAR(securities), commodity, wind);
1048 
1049  /* Add it to the list of security notebook pages. */
1050  wind->commodity_notebook_pages = g_list_append (wind->commodity_notebook_pages,
1051  new_comm_nb_page->notebook_page);
1052 
1053  gtk_widget_show_all (new_comm_nb_page->notebook_page);
1054  }
1055  wind->num_new_pages = wind->num_new_pages + 1;
1056  securities = SCM_CDR(securities);
1057  }
1058  gnc_unset_busy_cursor (NULL);
1059  PINFO ("Number of New Security pages is %d", wind->num_new_pages);
1060 }
1061 
1062 
1063 /****************************************************************
1064  * gnc_ui_qif_import_commodity_update
1065  *
1066  * This function updates the commodities based on the values for
1067  * mnemonic, namespace, and name approved by the user.
1068  ****************************************************************/
1069 static void
1070 gnc_ui_qif_import_commodity_update (QIFImportWindow * wind)
1071 {
1072  GList *pageptr;
1073  GtkWidget *notebook_page;
1074  QIFCommNotebookPage *comm_nb_page;
1075  const gchar *mnemonic = NULL;
1076  gchar *name_space = NULL;
1077  const gchar *fullname = NULL;
1078  gnc_commodity *tab_commodity;
1079 
1080  for (pageptr = wind->commodity_notebook_pages; pageptr; pageptr = pageptr->next)
1081  {
1082  notebook_page = pageptr->data;
1083  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
1084 
1085  /* Get any changes from the commodity page. */
1086  mnemonic = gtk_entry_get_text (GTK_ENTRY(comm_nb_page->mnemonic_entry));
1087  name_space = gnc_ui_namespace_picker_ns (comm_nb_page->namespace_combo);
1088  fullname = gtk_entry_get_text (GTK_ENTRY(comm_nb_page->name_entry));
1089 
1090  /* Update the commodity with the new values. */
1091  gnc_commodity_set_namespace (comm_nb_page->commodity, name_space);
1092  gnc_commodity_set_fullname (comm_nb_page->commodity, fullname);
1093  gnc_commodity_set_mnemonic (comm_nb_page->commodity, mnemonic);
1094 
1095  /* Add the commodity to the commodity table (if it isn't a duplicate). */
1096  tab_commodity = gnc_commodity_table_lookup (gnc_get_current_commodities(),
1097  name_space, mnemonic);
1098  if (!tab_commodity || tab_commodity == comm_nb_page->commodity)
1099  tab_commodity = gnc_commodity_table_insert (gnc_get_current_commodities(),
1100  comm_nb_page->commodity);
1101 
1102  /* Update the security hash table. */
1103  scm_hash_set_x (wind->security_hash,
1104  comm_nb_page->hash_key,
1105  SWIG_NewPointerObj (tab_commodity,
1106  SWIG_TypeQuery("_p_gnc_commodity"), 0));
1107 
1108  g_free (name_space);
1109  }
1110 }
1111 
1112 static void
1113 _gfec_error_handler (const char *message)
1114 {
1115  PERR ("qif-import:qif-to-gnc-undo encountered an error: %s", message);
1116 }
1117 
1118 /****************************************************************
1119  * gnc_ui_qif_import_convert_undo
1120  *
1121  * This function launches the Scheme procedure that un-imports
1122  * any imported accounts and transactions.
1123  ****************************************************************/
1124 static void
1125 gnc_ui_qif_import_convert_undo (QIFImportWindow * wind)
1126 {
1127  SCM undo = scm_c_eval_string ("qif-import:qif-to-gnc-undo");
1128 
1129  gnc_set_busy_cursor (NULL, TRUE);
1130 
1131  /* Undo the conversion. */
1132  if (wind->imported_account_tree != SCM_BOOL_F)
1133  gfec_apply (undo, scm_list_1 (wind->imported_account_tree),
1134  _gfec_error_handler);
1135 
1136  /* There's no imported account tree any more. */
1137  scm_gc_unprotect_object (wind->imported_account_tree);
1138  wind->imported_account_tree = SCM_BOOL_F;
1139  scm_gc_protect_object (wind->imported_account_tree);
1140 
1141  /* Get rid of the list of matched transactions. */
1142  scm_gc_unprotect_object (wind->match_transactions);
1143  wind->match_transactions = SCM_BOOL_F;
1144  scm_gc_protect_object (wind->match_transactions);
1145 
1146  gnc_unset_busy_cursor (NULL);
1147 }
1148 
1149 
1150 /****************************************************************
1151  * refresh_old_transactions
1152  *
1153  * This function launches the Scheme procedure that refreshes
1154  * the old transactions.
1155  ****************************************************************/
1156 static void
1157 refresh_old_transactions (QIFImportWindow * wind, int selection)
1158 {
1159  SCM possible_matches;
1160  SCM current_xtn;
1161  SCM selected;
1162  Transaction * gnc_xtn;
1163  Split * gnc_split;
1164  const gchar * amount_str;
1165  int rownum = 0;
1166  GtkTreeView *view;
1167  GtkListStore *store;
1168  GtkTreeIter iter;
1169  static GMutex mutex;
1170  if (!g_mutex_trylock(&mutex))
1171  return;
1172 
1173  view = GTK_TREE_VIEW(wind->old_transaction_view);
1174  store = GTK_LIST_STORE(gtk_tree_view_get_model (view));
1175  gtk_list_store_clear (store);
1176  g_mutex_unlock (&mutex);
1177 
1178  if (wind->match_transactions != SCM_BOOL_F)
1179  {
1180  possible_matches = SCM_CDR(scm_list_ref (wind->match_transactions,
1181  scm_from_int (wind->selected_transaction)));
1182  scm_call_2 (scm_c_eval_string ("qif-import:refresh-match-selection"),
1183  possible_matches, scm_from_int (selection));
1184 
1185  while (!scm_is_null (possible_matches))
1186  {
1187  gdouble amount_gd = 0;
1188  char datebuff [MAX_DATE_LENGTH + 1];
1189  memset (datebuff, 0, sizeof (datebuff));
1190  current_xtn = SCM_CAR(possible_matches);
1191 #define FUNC_NAME "xaccTransCountSplits"
1192  gnc_xtn = SWIG_MustGetPtr (SCM_CAR(current_xtn),
1193  SWIG_TypeQuery ("_p_Transaction"), 1, 0);
1194 #undef FUNC_NAME
1195  selected = SCM_CDR(current_xtn);
1196 
1197  if (xaccTransCountSplits (gnc_xtn) > 2)
1198  {
1199  amount_str = _("(split)");
1200  }
1201  else
1202  {
1203  gnc_split = xaccTransGetSplit(gnc_xtn, 0);
1204  amount_str =
1206  gnc_account_print_info
1207  (xaccSplitGetAccount (gnc_split), TRUE));
1208  amount_gd = gnc_numeric_to_double (xaccSplitGetValue(gnc_split));
1209  }
1210 
1211  gtk_list_store_append (store, &iter);
1213  xaccTransRetDatePosted (gnc_xtn));
1214  gtk_list_store_set
1215  (store, &iter,
1216  QIF_TRANS_COL_INDEX, rownum++,
1217  QIF_TRANS_COL_DATE, datebuff,
1218  QIF_TRANS_COL_DATE_INT64, xaccTransRetDatePosted(gnc_xtn), // used for sorting
1219  QIF_TRANS_COL_DESCRIPTION, xaccTransGetDescription (gnc_xtn),
1220  QIF_TRANS_COL_AMOUNT, amount_str,
1221  QIF_TRANS_COL_AMOUNT_DOUBLE, amount_gd, // used for sorting
1222  QIF_TRANS_COL_CHECKED, selected != SCM_BOOL_F,
1223  -1);
1224 
1225  possible_matches = SCM_CDR(possible_matches);
1226  }
1227  }
1228 }
1229 
1230 
1231 /****************************************************************
1232  * gnc_ui_qif_import_duplicate_new_select_cb
1233  *
1234  * This function is the call back for duplicate transactions.
1235  ****************************************************************/
1236 static void
1237 gnc_ui_qif_import_duplicate_new_select_cb (GtkTreeSelection *selection,
1238  QIFImportWindow *wind)
1239 {
1240  GtkTreeModel *model;
1241  GtkTreeIter iter;
1242 
1243  if (gtk_tree_selection_get_selected (selection, &model, &iter))
1244  gtk_tree_model_get (model, &iter, QIF_TRANS_COL_INDEX,
1245  &wind->selected_transaction, -1);
1246  refresh_old_transactions (wind, -1);
1247 }
1248 
1249 
1250 /****************************************************************
1251  * reset_ignore_old_select
1252  ****************************************************************/
1253 static gboolean
1254 reset_ignore_old_select (gboolean *ignore)
1255 {
1256  *ignore = FALSE;
1257  return FALSE;
1258 }
1259 
1260 
1261 /****************************************************************
1262  * gnc_ui_qif_import_duplicate_old_select_cb
1263  *
1264  * This function is the call back for duplicate transactions.
1265  ****************************************************************/
1266 static void
1267 gnc_ui_qif_import_duplicate_old_select_cb (GtkTreeSelection *selection,
1268  QIFImportWindow *wind)
1269 {
1270  GtkTreeModel *model;
1271  GtkTreeIter iter;
1272  gint row;
1273  static gboolean ignore_old_select = FALSE;
1274 
1275  /* Get the current selection then clear it. We're about to clear
1276  * the entire list store and rebuild it so this prevents errors. */
1277  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1278  return;
1279  gtk_tree_selection_unselect_all (selection);
1280 
1281  /* Getting a weird double call the first time a line is clicked.
1282  * Once via gtk_tree_view_button_press and then again via
1283  * gtk_tree_view_grab_focus. */
1284  if (ignore_old_select)
1285  return;
1286  ignore_old_select = TRUE;
1287  g_idle_add ((GSourceFunc)reset_ignore_old_select, &ignore_old_select);
1288 
1289  /* Get the row the user clicked on and update the scheme
1290  * code/rebuild the list store. */
1291  gtk_tree_model_get (model, &iter, QIF_TRANS_COL_INDEX, &row, -1);
1292  refresh_old_transactions (wind, row);
1293 }
1294 
1295 
1296 /********************************************************************
1297  * gnc_ui_qif_import_check_acct_tree
1298  *
1299  * Designed for use with gnc_main_window_foreach_page(), this
1300  * function determines whether an account tab is open in the main
1301  * window. The parameter user_data must point to a gboolean.
1302  ********************************************************************/
1303 static void
1304 gnc_ui_qif_import_check_acct_tree (GncPluginPage *page, gpointer user_data)
1305 {
1306  gboolean *found = user_data;
1307 
1308  if (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page) && found)
1309  *found = TRUE;
1310 }
1311 
1312 
1313 /****************************************************************
1314  * do_cancel
1315  *
1316  * Clears out any imported data and shuts down the importer.
1317  ****************************************************************/
1318 static void
1319 do_cancel (QIFImportWindow * wind)
1320 {
1321  GList *pageptr;
1322  GtkWidget *notebook_page;
1323  QIFCommNotebookPage *comm_nb_page;
1324  gnc_commodity_table *table;
1325 
1326  gnc_set_busy_cursor (NULL, TRUE);
1327 
1328  /* Remove any converted data. */
1329  gnc_ui_qif_import_convert_undo (wind);
1330 
1331  /* Remove any commodities created for assistant pages. */
1332  for (pageptr = wind->commodity_notebook_pages; pageptr; pageptr = pageptr->next)
1333  {
1334  notebook_page = pageptr->data;
1335  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
1336  gnc_commodity_destroy (comm_nb_page->commodity);
1337  }
1338 
1339  /* Remove any namespaces created by the user. */
1340  table = gnc_get_current_commodities ();
1341  while (wind->new_namespaces)
1342  {
1343  gnc_commodity_table_delete_namespace (table, (gchar *) wind->new_namespaces->data);
1344 
1345  /* Free the data and the list element. */
1346  g_free (wind->new_namespaces->data);
1347  wind->new_namespaces = g_list_delete_link (wind->new_namespaces,
1348  wind->new_namespaces);
1349  }
1350  gnc_unset_busy_cursor (NULL);
1351 
1352  /* Destroy the assistant. */
1353  gnc_close_gui_component_by_data (ASSISTANT_QIF_IMPORT_CM_CLASS, wind);
1354 }
1355 
1356 
1357 /****************************************************************
1358  * cancel_timeout_cb
1359  *
1360  * This timer callback function waits until the busy flag
1361  * has been cleared before acting to cancel the import.
1362  ****************************************************************/
1363 static gboolean
1364 cancel_timeout_cb (gpointer data)
1365 {
1366  QIFImportWindow *wind = data;
1367 
1368  if (wind->busy)
1369  /* Wait for timer to go off again. */
1370  return TRUE;
1371 
1372  /* The busy flag was lowered. Perform the cancel. */
1373  do_cancel (wind);
1374 
1375  /* Cancel the timer. */
1376  return FALSE;
1377 }
1378 
1379 
1380 /****************************************************************
1381  * gnc_ui_qif_import_cancel_cb
1382  *
1383  * Invoked when the "Cancel" button is clicked.
1384  ****************************************************************/
1385 void
1386 gnc_ui_qif_import_cancel_cb (GtkAssistant *gtkassistant, gpointer user_data)
1387 {
1388  QIFImportWindow *wind = user_data;
1389  gint currentpage = gtk_assistant_get_current_page (gtkassistant);
1390  GtkWidget *mypage = gtk_assistant_get_nth_page (gtkassistant, currentpage);
1391  const char *pagename = gtk_buildable_get_name (GTK_BUILDABLE(mypage));
1392  const char *fmt = _("Are you sure you want to cancel?");
1393 
1394  if (!g_strcmp0 (pagename, "summary_page"))
1395  {
1396  /* Hitting the window close button on the summary page should not
1397  invoke a cancel action. The import has finished at that point. */
1398  gnc_ui_qif_import_close_cb (gtkassistant, user_data);
1399  }
1400  else
1401  {
1402  if (!gnc_verify_dialog (GTK_WINDOW(gtkassistant), FALSE, "%s", fmt))
1403  return;
1404 
1405  if (wind->busy)
1406  {
1407  /* Cancel any long-running Scheme operation. */
1408  scm_c_eval_string ("(qif-import:cancel)");
1409 
1410  /* Wait for the busy flag to be lowered. */
1411  g_timeout_add (200, cancel_timeout_cb, user_data);
1412  }
1413  else
1414  do_cancel (wind);
1415  }
1416 }
1417 
1418 
1419 /****************************************************************
1420  * gnc_ui_qif_import_close_cb
1421  *
1422  * Invoked when the "Close" button is clicked.
1423  ****************************************************************/
1424 void
1425 gnc_ui_qif_import_close_cb (GtkAssistant *gtkassistant, gpointer user_data)
1426 {
1427  QIFImportWindow *wind = user_data;
1428 
1429  /* If We did not have an account tree, lets save it */
1430  if (!wind->acct_tree_found)
1431  {
1432  qof_book_mark_session_dirty (gnc_get_current_book ());
1433  gnc_ui_file_access_for_save_as (gnc_ui_get_main_window (GTK_WIDGET(gtkassistant)));
1434  }
1435 
1436  gnc_close_gui_component_by_data (ASSISTANT_QIF_IMPORT_CM_CLASS, wind);
1437 }
1438 
1439 
1440 /****************************************************************
1441  * gnc_ui_qif_import_assistant_get_mappings
1442  *
1443  * SCM get mappings.
1444  ****************************************************************/
1445 SCM
1446 gnc_ui_qif_import_assistant_get_mappings (QIFImportWindow * w)
1447 {
1448  return scm_list_3 (w->acct_map_info,
1449  w->cat_map_info,
1450  w->memo_map_info);
1451 }
1452 
1453 /***************************************************************************
1454  * gnc_ui_qif_import_assistant_page_forward - custom page forward function.
1455  * This gives us the ability to skip pages that are not relevant.
1456  * GtkAssistant does not give us a custom back function, but
1457  * it tracks pages as it runs, and you end up with effective
1458  * support for the back button as well
1459  ***************************************************************************/
1460 static int gnc_ui_qif_import_assistant_page_forward (int current_page, gpointer data)
1461 {
1462  QIFImportWindow *wind = data;
1463  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
1464  int page_count = gtk_assistant_get_n_pages (assistant);
1465  int next_page = current_page;
1466 
1467  for (next_page = current_page + 1; next_page < page_count; next_page++)
1468  {
1469  GtkWidget *page = gtk_assistant_get_nth_page (assistant, next_page);
1470 
1471  /* If the 'stop the presses' flag is set, move all the way to the end.
1472  TODO: This does not allow for any chance to recover
1473  and try a different approach. That is the historic
1474  behavior, and a moderately hard problem to solve.
1475  See bug 698804
1476  */
1477  if (wind->load_stop && next_page < (page_count - 1))
1478  continue;
1479 
1480  if (!gnc_ui_qif_import_assistant_skip_page (assistant, page, wind))
1481  return next_page;
1482  }
1483 
1484  /* If nothing forward is visible, just don't move */
1485  return current_page;
1486 }
1487 
1488 /****************************************************************************
1489  * gnc_ui_qif_import_assistant_skip_page - page specific 'skip me' functions
1490  * We can write page specific functions which can determine if a
1491  * given page should be skipped. This function routes to the
1492  * appropriate callback for a given page.
1493  ****************************************************************************/
1494 static gboolean
1495 gnc_ui_qif_import_assistant_skip_page (GtkAssistant *assistant, GtkWidget *page, QIFImportWindow *wind)
1496 {
1497  const char *pagename = gtk_buildable_get_name (GTK_BUILDABLE(page));
1498  gboolean rv = FALSE;
1499 
1500  ENTER("Page %s", pagename);
1501 
1502  if (!g_strcmp0 (pagename, "date_format_page"))
1503  {
1504  /* Current page is date page */
1505  rv = gnc_ui_qif_import_skip_date_format (assistant, wind);
1506  }
1507  else if (!g_strcmp0 (pagename, "account_name_page"))
1508  {
1509  /* Current page is account page */
1510  rv = gnc_ui_qif_import_skip_account (assistant, wind);
1511  }
1512  else if (!g_strcmp0 (pagename, "account_doc_page"))
1513  {
1514  /* Current page is Account Doc. page */
1515  rv = gnc_ui_qif_import_skip_account_doc (wind);
1516  }
1517  else if (!g_strcmp0 (pagename, "category_doc_page"))
1518  {
1519  /* Current page is Category Doc. page */
1520  rv = gnc_ui_qif_import_skip_category_doc (wind);
1521  }
1522  else if (!g_strcmp0 (pagename, "category_match_page"))
1523  {
1524  /* Current page is Category Match page */
1525  rv = gnc_ui_qif_import_skip_category_match (wind);
1526  }
1527  else if (!g_strcmp0 (pagename, "memo_doc_page"))
1528  {
1529  /* Current page is Memo Doc. page */
1530  rv = gnc_ui_qif_import_skip_memo_doc (wind);
1531  }
1532  else if (!g_strcmp0 (pagename, "memo_match_page"))
1533  {
1534  /* Current page is Memo Match page */
1535  rv = gnc_ui_qif_import_skip_memo_match (wind);
1536  }
1537  else if (!g_strcmp0 (pagename, "commodity_page"))
1538  {
1539  /* Current page is Commodity page */
1540  rv = gnc_ui_qif_import_skip_commodity (wind);
1541  }
1542  else if (!g_strcmp0 (pagename, "duplicates_doc_page"))
1543  {
1544  /* Current page is Duplicates Doc page */
1545  rv = gnc_ui_qif_import_skip_duplicates_doc (wind);
1546  }
1547  else if (!g_strcmp0 (pagename, "duplicates_match_page"))
1548  {
1549  /* Current page is Duplicates Match page */
1550  rv = gnc_ui_qif_import_skip_duplicates_match (wind);
1551  }
1552 
1553  /* By default, we do not skip */
1554  LEAVE("%s", rv ? "Skipped" : "Not Skipped");
1555  return rv;
1556 }
1557 
1558 
1559 
1560 /* ================================================================== */
1561 /* */
1562 /* IMPORTER CREATION */
1563 /* */
1564 /* ================================================================== */
1565 
1566 /********************************************************************
1567  * get_preferences
1568  *
1569  * Get all user preferences related to QIF import.
1570  ********************************************************************/
1571 static void
1572 get_preferences (QIFImportWindow *wind)
1573 {
1574  gchar tmp_transaction_status = 'n';
1575 
1576  g_return_if_fail (wind);
1577 
1578  /* Get the user's preference for showing documentation pages. */
1579  wind->show_doc_pages =
1580  gnc_prefs_get_bool (GNC_PREFS_GROUP, GNC_PREF_SHOW_DOC);
1581 
1582  /* Clear / Reconcile transaction if not specified in QIF file. */
1583  if (gnc_prefs_get_bool (GNC_PREFS_GROUP, GNC_PREF_DEFAULT_TRANS_STATUS_CLEARED))
1584  tmp_transaction_status = 'c';
1585  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP, GNC_PREF_DEFAULT_TRANS_STATUS_RECONCILED))
1586  tmp_transaction_status = 'y';
1587 
1588  wind->transaction_status = SCM_MAKE_CHAR(tmp_transaction_status);
1589 }
1590 
1591 
1592 /********************************************************************
1593  * initialize_scheme
1594  *
1595  * Initialize all Scheme-controlled objects.
1596  ********************************************************************/
1597 static void
1598 initialize_scheme (QIFImportWindow *wind)
1599 {
1600  SCM load_map_prefs;
1601  SCM mapping_info;
1602  SCM create_ticker_map;
1603 
1604  g_return_if_fail (wind);
1605 
1606  /* Initialize Scheme variables. */
1607  wind->imported_files = SCM_EOL;
1608  wind->selected_file = SCM_BOOL_F;
1609  wind->gnc_acct_info = SCM_BOOL_F;
1610  wind->cat_display_info = SCM_BOOL_F;
1611  wind->cat_map_info = SCM_BOOL_F;
1612  wind->acct_display_info = SCM_BOOL_F;
1613  wind->acct_map_info = SCM_BOOL_F;
1614  wind->memo_display_info = SCM_BOOL_F;
1615  wind->memo_map_info = SCM_BOOL_F;
1616  wind->security_hash = SCM_BOOL_F;
1617  wind->security_prefs = SCM_BOOL_F;
1618  wind->new_securities = SCM_BOOL_F;
1619  wind->ticker_map = SCM_BOOL_F;
1620  wind->imported_account_tree = SCM_BOOL_F;
1621  wind->match_transactions = SCM_BOOL_F;
1622 
1623  /* Get the saved state of mappings from Quicken accounts and
1624  * categories to GnuCash accounts. */
1625  load_map_prefs = scm_c_eval_string ("qif-import:load-map-prefs");
1626  mapping_info = scm_call_0 (load_map_prefs); /* <- gets/creates session/book */
1627  wind->gnc_acct_info = scm_list_ref (mapping_info, scm_from_int (0));
1628  wind->acct_map_info = scm_list_ref (mapping_info, scm_from_int (1));
1629  wind->cat_map_info = scm_list_ref (mapping_info, scm_from_int (2));
1630  wind->memo_map_info = scm_list_ref (mapping_info, scm_from_int (3));
1631  wind->security_hash = scm_list_ref (mapping_info, scm_from_int (4));
1632  wind->security_prefs = scm_list_ref (mapping_info, scm_from_int (5));
1633 
1634  /* Get the initial ticker map. */
1635  create_ticker_map = scm_c_eval_string ("make-ticker-map");
1636  wind->ticker_map = scm_call_0 (create_ticker_map);
1637 
1638  /* Protect our data from garbage collection. */
1639  scm_gc_protect_object (wind->imported_files);
1640  scm_gc_protect_object (wind->selected_file);
1641  scm_gc_protect_object (wind->gnc_acct_info);
1642  scm_gc_protect_object (wind->cat_display_info);
1643  scm_gc_protect_object (wind->cat_map_info);
1644  scm_gc_protect_object (wind->memo_display_info);
1645  scm_gc_protect_object (wind->memo_map_info);
1646  scm_gc_protect_object (wind->acct_display_info);
1647  scm_gc_protect_object (wind->acct_map_info);
1648  scm_gc_protect_object (wind->security_hash);
1649  scm_gc_protect_object (wind->security_prefs);
1650  scm_gc_protect_object (wind->new_securities);
1651  scm_gc_protect_object (wind->ticker_map);
1652  scm_gc_protect_object (wind->imported_account_tree);
1653  scm_gc_protect_object (wind->match_transactions);
1654 }
1655 
1656 
1657 /*****************************************
1658  * Page 0 - Intro Page Page
1659  ****************************************/
1660 
1661 /********************************************************************
1662  * gnc_ui_qif_import_intro_prepare
1663  *
1664  * Prepare the intro page for display.
1665  ********************************************************************/
1666 void
1667 gnc_ui_qif_import_intro_prepare (GtkAssistant *assistant, gpointer user_data)
1668 {
1669  QIFImportWindow *wind = user_data;
1670  SCM unload = scm_c_eval_string ("qif-dialog:unload-qif-file");
1671  SCM files_list;
1672 
1673  /* Set load stop to FALSE */
1674  wind->load_stop = FALSE;
1675  wind->read_file_warnings = FALSE;
1676 
1677  files_list = scm_call_2 (unload, wind->selected_file, wind->imported_files);
1678 
1679  scm_gc_unprotect_object (wind->imported_files);
1680  wind->imported_files = files_list;
1681  scm_gc_protect_object (wind->imported_files);
1682 
1683  scm_gc_unprotect_object (wind->selected_file);
1684  wind->selected_file = SCM_BOOL_F;
1685  scm_gc_protect_object (wind->selected_file);
1686 }
1687 
1688 
1689 /*****************************************
1690  * Page 1 - Load File Page Procedures
1691  ****************************************/
1692 
1693 /********************************************************************
1694  * gnc_ui_qif_import_load_file_complete
1695  *
1696  * Do we have a file to load.
1697  ********************************************************************/
1698 static gboolean
1699 gnc_ui_qif_import_load_file_complete (GtkAssistant *assistant,
1700  gpointer user_data)
1701 {
1702  QIFImportWindow * wind = user_data;
1703  const gchar * path_to_load;
1704 
1705  /* Get the file name. */
1706  path_to_load = gtk_entry_get_text (GTK_ENTRY(wind->filename_entry));
1707 
1708  /* Validate the chosen filename. */
1709  if (strlen (path_to_load) == 0)
1710  gnc_error_dialog (GTK_WINDOW(assistant), "%s", _("Please select a file to load."));
1711  else if (g_access (path_to_load, R_OK) < 0)
1712  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
1713  _("File not found or read permission denied. "
1714  "Please select another file."));
1715  else
1716  {
1717  SCM qif_file_loaded = scm_c_eval_string("qif-dialog:qif-file-loaded?");
1718 
1719  /* See if the file is already loaded. */
1720  if (scm_call_2 (qif_file_loaded,
1721  scm_from_locale_string (path_to_load ? path_to_load : ""),
1722  wind->imported_files) == SCM_BOOL_T)
1723  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
1724  _("That QIF file is already loaded. "
1725  "Please select another file."));
1726  else
1727  {
1728  /* Passed all checks; proceed to the next page. */
1729  return TRUE;
1730  }
1731  }
1732  /* Stay on this page. */
1733  return FALSE;
1734 }
1735 
1736 
1737 /********************************************************************
1738  * gnc_ui_qif_import_load_file_prepare
1739  *
1740  * Prepare the load file page for display.
1741  ********************************************************************/
1742 void
1743 gnc_ui_qif_import_load_file_prepare (GtkAssistant *assistant, gpointer user_data)
1744 {
1745  QIFImportWindow * wind = user_data;
1746  const gchar * path_to_load;
1747  gboolean page_status = FALSE;
1748 
1749 
1750  /* Get the file name. */
1751  path_to_load = gtk_entry_get_text (GTK_ENTRY(wind->filename_entry));
1752 
1753  /* Calculate status for the Assistant "Next" Button */
1754  if (strlen (path_to_load) != 0)
1755  {
1756  page_status = gnc_ui_qif_import_load_file_complete (assistant, user_data);
1757  }
1758  mark_page_complete(assistant, page_status);
1759 }
1760 
1761 
1762 /********************************************************************
1763  * gnc_ui_qif_import_select_file_cb
1764  *
1765  * invoked when the "select file" button is clicked
1766  * this is just to pick a file name and reset-to-defaults all the
1767  * fields describing how to parse the file.
1768  ********************************************************************/
1769 void
1770 gnc_ui_qif_import_select_file_cb (GtkButton * button,
1771  gpointer user_data)
1772 {
1773  QIFImportWindow * wind = user_data;
1774 
1775  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
1776 
1777  GtkFileFilter *filter;
1778  char * new_file_name;
1779  char *file_name, *default_dir;
1780 
1781  /* Default to whatever's already present */
1782  default_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
1783 
1784  filter = gtk_file_filter_new ();
1785  gtk_file_filter_set_name (filter, "*.qif");
1786  gtk_file_filter_add_pattern (filter, "*.[Qq][Ii][Ff]");
1787  new_file_name = gnc_file_dialog (gnc_ui_get_gtk_window (GTK_WIDGET(button)),
1788  _("Select QIF File"),
1789  g_list_prepend (NULL, filter),
1790  default_dir,
1791  GNC_FILE_DIALOG_IMPORT);
1792  /* If NULL then the user cancelled the file dialog. */
1793  if (new_file_name == NULL)
1794  {
1795  g_free (default_dir);
1796  return;
1797  }
1798  else if (!g_path_is_absolute (new_file_name))
1799  {
1800  file_name = g_build_filename (default_dir, new_file_name, NULL);
1801  g_free (new_file_name);
1802  }
1803  else
1804  {
1805  file_name = new_file_name;
1806  /* Update the working directory */
1807  g_free (default_dir);
1808  default_dir = g_path_get_dirname (file_name);
1809  gnc_set_default_directory (GNC_PREFS_GROUP, default_dir);
1810  }
1811  g_free (default_dir);
1812 
1813  /* set the filename entry for what was selected */
1814  gtk_entry_set_text (GTK_ENTRY(wind->filename_entry), file_name);
1815  g_free (file_name);
1816 
1817  mark_page_complete (assistant,
1818  gnc_ui_qif_import_load_file_complete (assistant, user_data));
1819 }
1820 
1821 
1822 /*****************************************
1823  * Page 2 - Load Progress Page Procedures
1824  ****************************************/
1825 
1826 /********************************************************************
1827  * gnc_ui_qif_import_load_progress_pause_cb
1828  *
1829  * Invoked when the "Pause" button is clicked.
1830  ********************************************************************/
1831 void
1832 gnc_ui_qif_import_load_progress_pause_cb (GtkButton * button,
1833  gpointer user_data)
1834 {
1835  QIFImportWindow *wind = user_data;
1836  SCM toggle_pause = scm_c_eval_string ("qif-import:toggle-pause");
1837  SCM progress;
1838 
1839  if (!wind->busy)
1840  return;
1841 
1842  /* Create SCM for the progress helper. */
1843  progress = SWIG_NewPointerObj (wind->load_progress,
1844  SWIG_TypeQuery ("_p__GNCProgressDialog"),
1845  0);
1846 
1847  /* Pause (or resume) the currently running operation. */
1848  scm_call_1 (toggle_pause, progress);
1849 
1850  /* Swap the button label between pause and resume. */
1851  if (strcmp (gtk_button_get_label (button), _("_Resume")))
1852  {
1853  gtk_button_set_use_underline (button, TRUE);
1854  gtk_button_set_label (button, _("_Resume"));
1855  }
1856  else
1857  {
1858  gtk_button_set_use_underline (button, FALSE);
1859  gtk_button_set_label (button, _("P_ause"));
1860  }
1861 }
1862 
1863 
1864 /********************************************************************
1865  * gnc_ui_qif_import_load_progress_start_cb
1866  *
1867  * Invoked when the "Start" button is clicked.
1868  ********************************************************************/
1869 void
1870 gnc_ui_qif_import_load_progress_start_cb (GtkButton * button,
1871  gpointer user_data)
1872 {
1873  QIFImportWindow *wind = user_data;
1874  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
1875 
1876  const gchar * path_to_load;
1877 
1878  SCM make_qif_file = scm_c_eval_string ("make-qif-file");
1879  SCM qif_file_load = scm_c_eval_string ("qif-file:read-file");
1880  SCM qif_file_parse = scm_c_eval_string ("qif-file:parse-fields");
1881  SCM unload_qif_file = scm_c_eval_string ("qif-dialog:unload-qif-file");
1882  SCM parse_results = scm_c_eval_string ("qif-file:parse-fields-results");
1883  SCM scm_qiffile;
1884  SCM imported_files = SCM_EOL;
1885  SCM load_return = SCM_BOOL_F, parse_return = SCM_BOOL_F;
1886  SCM progress;
1887 
1888  /* Raise the busy flag so the assistant can't be canceled unexpectedly. */
1889  wind->busy = TRUE;
1890  gtk_widget_set_sensitive (wind->load_pause, TRUE);
1891 
1892  /* Get the file name. */
1893  path_to_load = gtk_entry_get_text (GTK_ENTRY(wind->filename_entry));
1894 
1895  /* Create the <qif-file> object. */
1896  scm_qiffile = scm_call_0 (make_qif_file);
1897  scm_gc_unprotect_object (wind->selected_file);
1898  wind->selected_file = scm_qiffile;
1899  scm_gc_protect_object (wind->selected_file);
1900  imported_files = scm_cons (scm_qiffile, wind->imported_files);
1901 
1902  /* Create SCM for the progress helper. */
1903  progress = SWIG_NewPointerObj (wind->load_progress,
1904  SWIG_TypeQuery ("_p__GNCProgressDialog"),
1905  0);
1906 
1907  /* Clear any previous pause or cancel state. */
1908  scm_c_eval_string ("(qif-import:reset-cancel-pause)");
1909 
1910  /*
1911  * Load the file.
1912  *
1913  * The loader returns:
1914  * success: ()
1915  * failure: (#f error-message)
1916  * warning: (#t error-message)
1917  * cancel: #t
1918  * exception: #f
1919  */
1920 
1921  /* This step will fill 70% of the bar. */
1922  gnc_progress_dialog_push (wind->load_progress, 0.7);
1923  load_return = scm_call_4 (qif_file_load,
1924  SCM_CAR(imported_files),
1925  scm_from_locale_string (path_to_load ? path_to_load : ""),
1926  wind->ticker_map,
1927  progress);
1928  gnc_progress_dialog_pop (wind->load_progress);
1929  if (load_return == SCM_BOOL_T)
1930  {
1931  /* Canceled by the user. */
1932 
1933  /* Disable the pause button. */
1934  gtk_widget_set_sensitive (wind->load_pause, FALSE);
1935 
1936  /* Inform the user. */
1937  gnc_progress_dialog_set_sub (wind->load_progress, _("Canceled"));
1938 
1939  wind->busy = FALSE;
1940  wind->load_stop = TRUE;
1941  }
1942  else if (load_return == SCM_BOOL_F || !scm_is_list (load_return))
1943  {
1944  /* A bug was detected. */
1945 
1946  /* Disable the pause button. */
1947  gtk_widget_set_sensitive (wind->load_pause, FALSE);
1948 
1949  /* Inform the user. */
1950  gnc_progress_dialog_append_log (wind->load_progress,
1951  _("An error occurred while loading the QIF file."));
1952  gnc_progress_dialog_set_sub (wind->load_progress, _("Failed"));
1953  gnc_progress_dialog_reset_value (wind->load_progress);
1954  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
1955  _("An error occurred while loading the QIF file."));
1956  /* FIXME: How should we request that the user report this problem? */
1957 
1958  wind->busy = FALSE;
1959  wind->load_stop = TRUE;
1960  }
1961  else if (!scm_is_null (load_return))
1962  {
1963  if (SCM_CAR(load_return) == SCM_BOOL_F)
1964  {
1965  imported_files = scm_call_2 (unload_qif_file, scm_qiffile, imported_files);
1966  scm_gc_unprotect_object (wind->imported_files);
1967  wind->imported_files = imported_files;
1968  scm_gc_protect_object (wind->imported_files);
1969 
1970  gnc_progress_dialog_set_sub (wind->load_progress, _("Failed"));
1971  gnc_progress_dialog_reset_value (wind->load_progress);
1972 
1973  gtk_widget_set_sensitive (wind->load_pause, FALSE);
1974  wind->busy = FALSE;
1975  wind->load_stop = TRUE;
1976  }
1977  else
1978  wind->read_file_warnings = TRUE;
1979  }
1980 
1981  /*
1982  * Parse the fields.
1983  *
1984  * The parser returns:
1985  * success: ()
1986  * failure: (#f . ((type . error) ...))
1987  * warning: (#t . ((type . error) ...))
1988  * cancel: #t
1989  * exception: #f
1990  */
1991 
1992  /* This step will fill the remainder of the bar. */
1993  if (!wind->load_stop)
1994  {
1995  gnc_progress_dialog_push (wind->load_progress, 1);
1996  parse_return = scm_call_2 (qif_file_parse, SCM_CAR(imported_files),
1997  progress);
1998  gnc_progress_dialog_pop (wind->load_progress);
1999  wind->ask_date_format = FALSE;
2000  wind->date_format = NULL;
2001  }
2002  if (parse_return == SCM_BOOL_T)
2003  {
2004  /* Canceled by the user. */
2005 
2006  /* Disable the pause button. */
2007  gtk_widget_set_sensitive (wind->load_pause, FALSE);
2008 
2009  /* Unload the file. */
2010  gnc_progress_dialog_set_sub (wind->load_progress, _("Cleaning up"));
2011  imported_files = scm_call_2 (unload_qif_file, scm_qiffile, imported_files);
2012 
2013  /* Inform the user. */
2014  gnc_progress_dialog_set_sub (wind->load_progress, _("Canceled"));
2015 
2016  wind->busy = FALSE;
2017  wind->load_stop = TRUE;
2018  }
2019  else if (parse_return == SCM_BOOL_F || !scm_is_list(parse_return))
2020  {
2021  /* A bug was detected. */
2022 
2023  /* Disable the pause button. */
2024  gtk_widget_set_sensitive (wind->load_pause, FALSE);
2025 
2026  /* Unload the file. */
2027  gnc_progress_dialog_set_sub (wind->load_progress, _("Cleaning up"));
2028  imported_files = scm_call_2 (unload_qif_file, scm_qiffile, imported_files);
2029 
2030  /* Inform the user. */
2031  gnc_progress_dialog_append_log (wind->load_progress,
2032  _("A bug was detected while parsing the QIF file."));
2033  gnc_progress_dialog_set_sub (wind->load_progress, _("Failed"));
2034  gnc_progress_dialog_reset_value (wind->load_progress);
2035  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
2036  _("A bug was detected while parsing the QIF file."));
2037  /* FIXME: How should we request that the user report this problem? */
2038 
2039  wind->busy = FALSE;
2040  wind->load_stop = TRUE;
2041  }
2042  else if (!scm_is_null (parse_return))
2043  {
2044  /* Are there only warnings? */
2045  if (SCM_CAR(parse_return) == SCM_BOOL_T)
2046  {
2047  SCM date_formats;
2048 
2049  /* A warning means that (potentially) the date format is
2050  * ambiguous. So search the results for the "date" type and if
2051  * it's found, set up the format selector page. */
2052  if ((date_formats = scm_call_2 (parse_results,
2053  SCM_CDR(parse_return),
2054  scm_from_locale_symbol ("date"))) != SCM_BOOL_F)
2055  {
2056  GtkComboBox *combo_box;
2057  GtkTreeModel *model;
2058  GtkTreeIter iter;
2059 
2060  /* Block the date call back */
2061  g_signal_handlers_block_by_func (wind->date_format_combo, gnc_ui_qif_import_date_valid_cb, wind);
2062 
2063  /* Clear the date format combo box. */
2064  combo_box = GTK_COMBO_BOX(wind->date_format_combo);
2065  model = gtk_combo_box_get_model (combo_box);
2066  gtk_list_store_clear (GTK_LIST_STORE(model));
2067 
2068  gtk_combo_box_set_active (GTK_COMBO_BOX(wind->date_format_combo), -1);
2069 
2070  /* Add the formats for the user to select from. */
2071  while (scm_is_list (date_formats) && !scm_is_null (date_formats))
2072  {
2073  gtk_list_store_append (GTK_LIST_STORE(model), &iter);
2074  gtk_list_store_set (GTK_LIST_STORE(model), &iter, 0, gnc_scm_symbol_to_locale_string (SCM_CAR(date_formats)), -1);
2075 
2076  date_formats = SCM_CDR(date_formats);
2077  }
2078 
2079  /* Unblock the date call back */
2080  g_signal_handlers_unblock_by_func (wind->date_format_combo, gnc_ui_qif_import_date_valid_cb, wind);
2081 
2082  wind->ask_date_format = TRUE;
2083  }
2084  wind->load_stop = TRUE;
2085  }
2086  else
2087  {
2088  /* Parsing failed. */
2089  imported_files = scm_call_2 (unload_qif_file, scm_qiffile, imported_files);
2090  gnc_progress_dialog_set_sub (wind->load_progress, _("Failed"));
2091  gnc_progress_dialog_reset_value (wind->load_progress);
2092 
2093  gtk_widget_set_sensitive (wind->load_pause, FALSE);
2094  wind->busy = FALSE;
2095  wind->load_stop = TRUE;
2096  }
2097  }
2098 
2099  /* Enable the assistant "Next" button */
2100  mark_page_complete (assistant, TRUE);
2101 
2102  /* Set Pause and Start buttons */
2103  gtk_widget_set_sensitive (wind->load_pause, FALSE);
2104  gtk_widget_set_sensitive (wind->load_start, FALSE);
2105 
2106  /* The file was loaded successfully. */
2107  gnc_progress_dialog_set_sub (wind->load_progress, _("Loading completed"));
2108  gnc_progress_dialog_set_value (wind->load_progress, 1);
2109 
2110  scm_gc_unprotect_object (wind->imported_files);
2111  wind->imported_files = imported_files;
2112  scm_gc_protect_object (wind->imported_files);
2113 
2114  gtk_widget_set_sensitive (wind->load_pause, FALSE);
2115  wind->busy = FALSE;
2116 
2117 
2118  if (wind->load_stop == FALSE && wind->read_file_warnings == FALSE)
2119  {
2120  /* Auto step to next page */
2121  gtk_assistant_next_page (assistant);
2122  }
2123  wind->load_stop = FALSE;
2124 }
2125 
2126 
2127 /********************************************************************
2128  * gnc_ui_qif_import_load_progress_prepare
2129  *
2130  * Prepare the file loading progress page for display.
2131  ********************************************************************/
2132 void
2133 gnc_ui_qif_import_load_progress_prepare (GtkAssistant *assistant, gpointer user_data)
2134 {
2135  QIFImportWindow *wind = user_data;
2136 
2137  /* Reset the progress display. */
2138  gnc_progress_dialog_set_primary (wind->load_progress, "");
2139  gnc_progress_dialog_set_secondary (wind->load_progress,
2140  _("When you press the Start Button, GnuCash will load your QIF file. If there are no errors or warnings, you will automatically proceed to the next step. Otherwise, the details will be shown below for your review."));
2141  gnc_progress_dialog_set_sub (wind->load_progress, " ");
2142  gnc_progress_dialog_reset_value (wind->load_progress);
2143  gnc_progress_dialog_reset_log (wind->load_progress);
2144 
2145  /* Set Pause and Start buttons */
2146  gtk_widget_set_sensitive (wind->load_pause, FALSE);
2147  gtk_widget_set_sensitive (wind->load_start, TRUE);
2148 
2149  /* Disable the assistant "Next" button */
2150  mark_page_complete (assistant, FALSE);
2151 }
2152 
2153 
2154 /*****************************************
2155  * Page 3 - Date format Page Procedures
2156  ****************************************/
2157 
2158 /********************************************************************
2159  * gnc_ui_qif_import_skip_date_format
2160  *
2161  * Determine if we need the date page
2162  ********************************************************************/
2163 static gboolean
2164 gnc_ui_qif_import_skip_date_format (GtkAssistant *assistant, QIFImportWindow *wind)
2165 {
2166  return ! wind->ask_date_format;
2167 }
2168 
2169 
2170 /********************************************************************
2171  * gnc_ui_qif_import_date_valid_cb
2172  *
2173  * Reparse file with new date format.
2174  ********************************************************************/
2175 static void
2176 qif_import_reparse_dates (QIFImportWindow* wind)
2177 {
2178  SCM reparse_dates = scm_c_eval_string ("qif-file:reparse-dates");
2179  SCM format_sym = scm_from_locale_symbol (wind->date_format);
2180 
2181  /* Reparse the dates using the selected format. */
2182  scm_call_2 (reparse_dates, wind->selected_file, format_sym);
2183  g_free (wind->date_format);
2184  wind->date_format = NULL;
2185  wind->ask_date_format = FALSE;
2186 }
2187 
2188 void
2189 gnc_ui_qif_import_date_valid_cb (GtkWidget *widget, gpointer user_data)
2190 {
2191  QIFImportWindow * wind = user_data;
2192  GtkTreeModel *model;
2193  GtkTreeIter iter;
2194 
2195  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
2196 
2197  /* Get the selected date format. */
2198  model = gtk_combo_box_get_model (GTK_COMBO_BOX(wind->date_format_combo));
2199  gtk_combo_box_get_active_iter (GTK_COMBO_BOX(wind->date_format_combo), &iter);
2200  gtk_tree_model_get (model, &iter, 0, &wind->date_format, -1);
2201 
2202  if (!wind->date_format)
2203  {
2204  g_critical ("QIF import: BUG DETECTED in gnc_ui_qif_import_date_valid_cb. Format is NULL.");
2205  }
2206 
2207  qif_import_reparse_dates (wind);
2208 
2209  mark_page_complete (assistant, TRUE);
2210 }
2211 
2212 /******************************************
2213  * Page 4 - Account Setup Page Procedures
2214  ******************************************/
2215 
2216 /********************************************************************
2217  * gnc_ui_qif_import_account_prepare
2218  *
2219  * Do we need to specify an account.
2220  ********************************************************************/
2221 void
2222 gnc_ui_qif_import_account_prepare (GtkAssistant *assistant, gpointer user_data)
2223 {
2224  QIFImportWindow * wind = user_data;
2225 
2226  SCM check_from_acct = scm_c_eval_string ("qif-file:check-from-acct");
2227 
2228  /* make sure there is a file selected, may have come back */
2229  if (wind->selected_file == SCM_BOOL_F)
2230  {
2231  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
2232  gtk_entry_set_text (GTK_ENTRY(wind->filename_entry), "");
2233  gtk_assistant_set_current_page (assistant, 1);
2234  }
2235  else
2236  {
2237  /* Determine the next page to display. */
2238  if (scm_call_1 (check_from_acct, wind->selected_file) != SCM_BOOL_T)
2239  {
2240  /* There is an account name missing. Ask the user to provide one. */
2241  SCM default_acct = scm_c_eval_string ("qif-file:path-to-accountname");
2242  gchar * default_acctname = NULL;
2243 
2244  default_acctname = gnc_scm_call_1_to_string (default_acct, wind->selected_file);
2245  gtk_entry_set_text (GTK_ENTRY(wind->acct_entry), default_acctname);
2246  g_free (default_acctname);
2247  }
2248  }
2249 }
2250 
2251 /********************************************************************
2252  * gnc_ui_qif_import_skip_account
2253  *
2254  * Determine if we need the import account page
2255  ********************************************************************/
2256 static gboolean
2257 gnc_ui_qif_import_skip_account (GtkAssistant *assistant, QIFImportWindow *wind)
2258 {
2259  SCM check_from_acct = scm_c_eval_string ("qif-file:check-from-acct");
2260  if (wind->selected_file != SCM_BOOL_F &&
2261  scm_call_1 (check_from_acct, wind->selected_file) == SCM_BOOL_T)
2262  return TRUE;
2263  return FALSE;
2264 }
2265 
2266 /********************************************************************
2267  * gnc_ui_qif_import_acct_enter_cb
2268  *
2269  * Invoked when the "enter" button is clicked on the acct entry.
2270  ********************************************************************/
2271 void
2272 gnc_ui_qif_import_acct_enter_cb (GtkWidget * widget,
2273  gpointer user_data)
2274 {
2275  QIFImportWindow * wind = user_data;
2276 
2277  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
2278 
2279  const gchar * acct_name = gtk_entry_get_text (GTK_ENTRY(wind->acct_entry));
2280 
2281  if (!acct_name || acct_name[0] == 0)
2282  {
2283  /* Disable the assistant "Next" Button */
2284  mark_page_complete (assistant, FALSE);
2285  }
2286  else
2287  {
2288  /* Enable the assistant "Next" Button and proceed */
2289  mark_page_complete (assistant, TRUE);
2290 
2291  /* Move on to the next page automatically */
2292  gtk_assistant_next_page (assistant);
2293  }
2294 }
2295 
2296 
2297 /********************************************************************
2298  * gnc_ui_qif_import_acct_valid_cb
2299  *
2300  * Change signal for the acct entry to enable "Next" button.
2301  ********************************************************************/
2302 void
2303 gnc_ui_qif_import_acct_valid_cb (GtkWidget * widget,
2304  gpointer user_data)
2305 {
2306  QIFImportWindow * wind = user_data;
2307 
2308  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
2309 
2310  const gchar * acct_name = gtk_entry_get_text (GTK_ENTRY(wind->acct_entry));
2311 
2312  if (!acct_name || acct_name[0] == 0)
2313  {
2314  /* Disable the assistant "Next" Button */
2315  mark_page_complete (assistant, FALSE);
2316  }
2317  else
2318  {
2319  /* Enable the assistant "Next" Button */
2320  mark_page_complete (assistant, TRUE);
2321  }
2322 }
2323 
2324 
2325 /*****************************************
2326  * Page 5 - Loaded Files Page Procedures
2327  ****************************************/
2328 
2329 /********************************************************************
2330  * gnc_ui_qif_import_loaded_files_prepare
2331  *
2332  * Get the loaded files page ready for viewing
2333  ********************************************************************/
2334 void
2335 gnc_ui_qif_import_loaded_files_prepare (GtkAssistant *assistant,
2336  gpointer user_data)
2337 {
2338  QIFImportWindow * wind = user_data;
2339 
2340  const gchar * acct_name = gtk_entry_get_text (GTK_ENTRY(wind->acct_entry));
2341  SCM fix_default = scm_c_eval_string ("qif-import:fix-from-acct");
2342  SCM scm_name;
2343 
2344  if (wind->selected_file != SCM_BOOL_F)
2345  {
2346  scm_name = scm_from_utf8_string (acct_name ? acct_name : "");
2347  scm_call_2 (fix_default, wind->selected_file, scm_name);
2348 
2349  /* Enable the assistant "Next" Button */
2350  mark_page_complete (assistant, TRUE);
2351  }
2352 
2353  update_file_page (wind);
2354 }
2355 
2356 
2357 /********************************************************************
2358  * gnc_ui_qif_import_load_another_cb
2359  * Invoked when the "load another" button is clicked on the loaded
2360  * files page.
2361  ********************************************************************/
2362 void
2363 gnc_ui_qif_import_load_another_cb (GtkButton * button,
2364  gpointer user_data)
2365 {
2366  QIFImportWindow * wind = user_data;
2367  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
2368 
2369  gtk_entry_set_text (GTK_ENTRY(wind->filename_entry), "");
2370 
2371  gtk_assistant_set_current_page (assistant, 1);
2372 }
2373 
2374 
2375 /********************************************************************
2376  * gnc_ui_qif_import_unload_cb
2377  * Invoked when the "unload" button is clicked on the loaded files
2378  * page.
2379  ********************************************************************/
2380 void
2381 gnc_ui_qif_import_unload_file_cb (GtkButton * button,
2382  gpointer user_data)
2383 {
2384  QIFImportWindow * wind = user_data;
2385 
2386  SCM unload_qif_file = scm_c_eval_string ("qif-dialog:unload-qif-file");
2387  SCM imported_files;
2388 
2389  if (wind->selected_file != SCM_BOOL_F)
2390  {
2391  imported_files =
2392  scm_call_2 (unload_qif_file, wind->selected_file, wind->imported_files);
2393 
2394  scm_gc_unprotect_object (wind->imported_files);
2395  wind->imported_files = imported_files;
2396  scm_gc_protect_object (wind->imported_files);
2397 
2398  scm_gc_unprotect_object (wind->selected_file);
2399  wind->selected_file = SCM_BOOL_F;
2400  scm_gc_protect_object (wind->selected_file);
2401 
2402  update_file_page (wind);
2403  }
2404 }
2405 
2406 
2407 /********************************************************************
2408  * update_file_page
2409  *
2410  * Update the list of loaded files.
2411  ********************************************************************/
2412 static void
2413 update_file_page (QIFImportWindow * wind)
2414 {
2415  SCM loaded_file_list = wind->imported_files;
2416  SCM qif_file_path;
2417  int row = 0;
2418  GtkTreeView *view;
2419  GtkListStore *store;
2420  GtkTreeIter iter;
2421  GtkTreePath *path;
2422  GtkTreeRowReference *reference = NULL;
2423 
2424  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
2425  gint num_of_files = 0;
2426 
2427  /* clear the list */
2428  view = GTK_TREE_VIEW(wind->selected_file_view);
2429  store = GTK_LIST_STORE(gtk_tree_view_get_model (view));
2430  gtk_list_store_clear (store);
2431  qif_file_path = scm_c_eval_string ("qif-file:path");
2432 
2433  mark_page_complete (assistant, FALSE);
2434 
2435  while (!scm_is_null (loaded_file_list))
2436  {
2437  gchar *row_text = NULL;
2438  SCM scm_qiffile = SCM_BOOL_F;
2439 
2440  scm_qiffile = SCM_CAR(loaded_file_list);
2441  row_text = gnc_scm_call_1_to_string (qif_file_path, scm_qiffile);
2442 
2443  gtk_list_store_append (store, &iter);
2444  gtk_list_store_set (store, &iter,
2445  FILENAME_COL_INDEX, row++,
2446  FILENAME_COL_NAME, row_text,
2447  -1);
2448  g_free (row_text);
2449 
2450  if (scm_qiffile == wind->selected_file)
2451  {
2452  path = gtk_tree_model_get_path (GTK_TREE_MODEL(store), &iter);
2453  reference = gtk_tree_row_reference_new (GTK_TREE_MODEL(store), path);
2454  gtk_tree_path_free (path);
2455  }
2456  loaded_file_list = SCM_CDR(loaded_file_list);
2457  }
2458 
2459  if (reference)
2460  {
2461  GtkTreeSelection* selection = gtk_tree_view_get_selection (view);
2462  path = gtk_tree_row_reference_get_path (reference);
2463  if (path)
2464  {
2465  gtk_tree_selection_select_path (selection, path);
2466  gtk_tree_path_free (path);
2467  }
2468  gtk_tree_row_reference_free (reference);
2469  }
2470 
2471  /* get the number of files in the list */
2472  num_of_files = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(store), NULL);
2473 
2474  if (num_of_files > 0)
2475  mark_page_complete (assistant, TRUE);
2476  else
2477  {
2478  /* TODO: It would be ideal to disable the back button at this point
2479  until all files have been unloaded. However, GtkAssistant does
2480  not provide a way to do that.
2481 
2482  The back button works at this point, but results in mildly
2483  confusing behavior - you get an error on the select page,
2484  and you are forced to load another file; you can't just skip
2485  forward and back. Fixing that may be possible; changing the
2486  load page to more intelligently handle the case where the selected
2487  file is already loaded should work. But that will be fiddly,
2488  as you likely want to force an already loaded file to be reloaded
2489  as we come forward. The current muddle 'feels' bad, but gives
2490  a user a fairly clear understanding of what is happening, and
2491  so I am choosing to prefer it.
2492  */
2493  }
2494 
2495 }
2496 
2497 
2498 /**********************************************
2499  * Page 6 - Account Doc. Page Procedures
2500  **********************************************/
2501 
2502 /********************************************************************
2503  * gnc_ui_qif_import_account_doc_prepare
2504  ********************************************************************/
2505 void
2506 gnc_ui_qif_import_account_doc_prepare (GtkAssistant *assistant,
2507  gpointer user_data)
2508 {
2509  /* Enable the Assistant "Next" Button */
2510  mark_page_complete (assistant, TRUE);
2511 }
2512 
2513 /********************************************************************
2514  * gnc_ui_qif_import_skip_account_doc
2515  *
2516  * Determine if we need the import account doc page
2517  ********************************************************************/
2518 static gboolean
2519 gnc_ui_qif_import_skip_account_doc (QIFImportWindow *wind)
2520 {
2521  return !wind->show_doc_pages;
2522 }
2523 
2524 /******************************************
2525  * Page 7 - Account Match Page Procedures
2526  ******************************************/
2527 
2528 /********************************************************************
2529  * gnc_ui_qif_import_account_match_prepare
2530  *
2531  * Get the matching pages ready for viewing.
2532  ********************************************************************/
2533 void
2534 gnc_ui_qif_import_account_match_prepare (GtkAssistant *assistant,
2535  gpointer user_data)
2536 {
2537  QIFImportWindow * wind = user_data;
2538 
2539  /* Prepare the matching pages. */
2540  gnc_set_busy_cursor (NULL, TRUE);
2541  update_account_page (wind);
2542  update_category_page (wind);
2543  update_memo_page (wind);
2544  gnc_unset_busy_cursor (NULL);
2545 
2546  /* Enable the Assistant "Next" Button */
2547  mark_page_complete (assistant, TRUE);
2548 }
2549 
2550 
2551 /****************************************************************
2552  * gnc_ui_qif_import_account_rematch_cb
2553  *
2554  * This handler is invoked when the user clicks the "Change
2555  * GnuCash account" button on the account mapping page. This
2556  * button is an alternative to double-clicking a row.
2557  ****************************************************************/
2558 void
2559 gnc_ui_qif_import_account_rematch_cb (GtkButton *button, gpointer user_data)
2560 {
2561  QIFImportWindow *wind = user_data;
2562 
2563  g_return_if_fail (wind);
2564 
2565  rematch_line (wind,
2566  gtk_tree_view_get_selection (GTK_TREE_VIEW(wind->acct_view)),
2567  wind->acct_display_info,
2568  wind->acct_map_info,
2569  update_account_page);
2570 }
2571 
2572 
2573 /*******************************************
2574  * Page 8 - Category Doc. Page Procedures
2575  *******************************************/
2576 
2577 /********************************************************************
2578  * gnc_ui_qif_import_category_doc_prepare
2579  ********************************************************************/
2580 void
2581 gnc_ui_qif_import_category_doc_prepare (GtkAssistant *assistant,
2582  gpointer user_data)
2583 {
2584  /* Enable the Assistant "Next" Button */
2585  mark_page_complete (assistant, TRUE);
2586 }
2587 
2588 /********************************************************************
2589  * gnc_ui_qif_import_skip_category_doc
2590  *
2591  * Determine if we need the import category doc page
2592  ********************************************************************/
2593 static gboolean
2594 gnc_ui_qif_import_skip_category_doc (QIFImportWindow *wind)
2595 {
2596  /* Jump over doc page if show_doc_pages FALSE */
2597  if (!wind->show_doc_pages)
2598  return TRUE;
2599 
2600  /* If there are no category mappings, jump the doc page. */
2601  if (scm_is_list (wind->cat_display_info) && scm_is_null (wind->cat_display_info))
2602  return TRUE;
2603 
2604  return FALSE;
2605 }
2606 
2607 
2608 /******************************************
2609  * Page 9 - Category Match Page Procedures
2610  ******************************************/
2611 
2612 /****************************************************************
2613  * gnc_ui_qif_import_category_match_prepare
2614  *
2615  * Find the next page to show, depending on whether there are
2616  * category or payee/memo mappings to be dealt with.
2617  ****************************************************************/
2618 void
2619 gnc_ui_qif_import_category_match_prepare (GtkAssistant *assistant,
2620  gpointer user_data)
2621 {
2622  /* Enable the Assistant "Next" Button */
2623  mark_page_complete (assistant, TRUE);
2624 }
2625 
2626 /********************************************************************
2627  * gnc_ui_qif_import_skip_category_match
2628  *
2629  * Determine if we need the import category match page
2630  ********************************************************************/
2631 static gboolean
2632 gnc_ui_qif_import_skip_category_match (QIFImportWindow *wind)
2633 {
2634  /* If there are no category mappings, jump this step. */
2635  if (scm_is_list (wind->cat_display_info) && scm_is_null (wind->cat_display_info))
2636  return TRUE;
2637 
2638  return FALSE;
2639 }
2640 
2641 
2642 /****************************************************************
2643  * gnc_ui_qif_import_category_rematch_cb
2644  *
2645  * This handler is invoked when the user clicks the "Change
2646  * GnuCash account" button on the category mapping page. This
2647  * button is an alternative to double-clicking a row.
2648  ****************************************************************/
2649 void
2650 gnc_ui_qif_import_category_rematch_cb (GtkButton *button, gpointer user_data)
2651 {
2652  QIFImportWindow *wind = user_data;
2653 
2654  g_return_if_fail (wind);
2655 
2656  rematch_line (wind,
2657  gtk_tree_view_get_selection (GTK_TREE_VIEW(wind->cat_view)),
2658  wind->cat_display_info,
2659  wind->cat_map_info,
2660  update_category_page);
2661 }
2662 
2663 
2664 /****************************************
2665  * Page 10 - Memo Doc. Page Procedures
2666  ****************************************/
2667 
2668 /********************************************************************
2669  * gnc_ui_qif_import_memo_doc_prepare
2670  ********************************************************************/
2671 void
2672 gnc_ui_qif_import_memo_doc_prepare (GtkAssistant *assistant, gpointer user_data)
2673 {
2674  /* Enable the Assistant "Next" Button */
2675  mark_page_complete (assistant, TRUE);
2676 }
2677 
2678 /********************************************************************
2679  * gnc_ui_qif_import_skip_memo_doc
2680  *
2681  * Determine if we need the import memo doc page
2682  ********************************************************************/
2683 static gboolean
2684 gnc_ui_qif_import_skip_memo_doc (QIFImportWindow *wind)
2685 {
2686  /* Jump over doc page if show_doc_pages FALSE */
2687  if (!wind->show_doc_pages)
2688  return TRUE;
2689 
2690  /* If there are no memo mappings, jump the doc page. */
2691  if (scm_is_list (wind->memo_display_info) && scm_is_null (wind->memo_display_info))
2692  return TRUE;
2693 
2694  return FALSE;
2695 }
2696 
2697 
2698 /****************************************
2699  * Page 11 - Memo Match Page Procedures
2700  ****************************************/
2701 
2702 /****************************************************************
2703  * gnc_ui_qif_import_memo_match_prepare
2704  *
2705  * Find the next page to show, depending on whether there are
2706  * category or payee/memo mappings to be dealt with.
2707  ****************************************************************/
2708 void
2709 gnc_ui_qif_import_memo_match_prepare (GtkAssistant *assistant, gpointer user_data)
2710 {
2711  /* Enable the Assistant "Next" Button */
2712  mark_page_complete (assistant, TRUE);
2713 }
2714 
2715 /********************************************************************
2716  * gnc_ui_qif_import_skip_memo_match
2717  *
2718  * Determine if we need the import memo match page
2719  ********************************************************************/
2720 static gboolean
2721 gnc_ui_qif_import_skip_memo_match (QIFImportWindow *wind)
2722 {
2723  /* If there are no memo mappings, jump this step. */
2724  if (scm_is_list (wind->memo_display_info) && scm_is_null (wind->memo_display_info))
2725  return TRUE;
2726 
2727  return FALSE;
2728 }
2729 
2730 
2731 /****************************************************************
2732  * gnc_ui_qif_import_memo_rematch_cb
2733  *
2734  * This handler is invoked when the user clicks the "Change
2735  * GnuCash account" button on the memo mapping page. This
2736  * button is an alternative to double-clicking a row.
2737  ****************************************************************/
2738 void
2739 gnc_ui_qif_import_memo_rematch_cb (GtkButton *button, gpointer user_data)
2740 {
2741  QIFImportWindow * wind = user_data;
2742 
2743  g_return_if_fail (wind);
2744 
2745  rematch_line (wind,
2746  gtk_tree_view_get_selection (GTK_TREE_VIEW(wind->memo_view)),
2747  wind->memo_display_info,
2748  wind->memo_map_info,
2749  update_memo_page);
2750 }
2751 
2752 
2753 /*****************************************
2754  * Page 12 - Currency Page Procedures
2755  ****************************************/
2756 
2757 /****************************************************************
2758  * gnc_ui_qif_import_currency_prepare
2759  *
2760  * Find the next page to show, depending on whether there are
2761  * category or payee/memo mappings to be dealt with.
2762  ****************************************************************/
2763 void
2764 gnc_ui_qif_import_currency_prepare (GtkAssistant *assistant, gpointer user_data)
2765 {
2766  gint num = gtk_assistant_get_current_page (assistant);
2767  GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
2768  QIFImportWindow *wind = user_data;
2769 
2770  g_return_if_fail (wind);
2771 
2772  /* Only display Book Option data if new book */
2773  if (wind->new_book)
2774  {
2775  gtk_assistant_set_page_title (assistant, page,
2776  _("Choose the QIF file currency and select Book Options"));
2777  gtk_widget_show (wind->book_option_label);
2778  gtk_widget_show (wind->book_option_message);
2779  }
2780  else
2781  {
2782  gtk_assistant_set_page_title (assistant, page,
2783  _("Choose the QIF file currency"));
2784  gtk_widget_hide (wind->book_option_label);
2785  gtk_widget_hide (wind->book_option_message);
2786  }
2787 
2788  /* Enable the Assistant "Next" Button */
2789  mark_page_complete (assistant, TRUE);
2790 }
2791 
2792 
2793 /**************************************************
2794  * Page 13 - Commodity Page Procedures
2795  **************************************************/
2796 
2797 /****************************************************************
2798  * gnc_ui_qif_import_new_securities
2799  *
2800  * This function creates or updates the list of QIF securities
2801  * for which no corresponding GnuCash commodity existed prior to
2802  * import. If there are any such securities, TRUE is returned.
2803  * Otherwise, FALSE is returned.
2804  ****************************************************************/
2805 static gboolean
2806 gnc_ui_qif_import_new_securities (QIFImportWindow * wind)
2807 {
2808  SCM updates;
2809  SCM update_securities = scm_c_eval_string ("qif-import:update-security-hash");
2810 
2811  /* Get a list of any new QIF securities since the previous call. */
2812  updates = scm_call_4 (update_securities,
2813  wind->security_hash,
2814  wind->ticker_map,
2815  wind->acct_map_info,
2816  wind->security_prefs);
2817  if (updates != SCM_BOOL_F)
2818  {
2819  /* A list of new QIF securities was returned. Save it. */
2820  scm_gc_unprotect_object (wind->new_securities);
2821  if (wind->new_securities != SCM_BOOL_F)
2822  /* There is an existing list, so append the new list. */
2823  wind->new_securities = scm_append (scm_list_2 (wind->new_securities,
2824  updates));
2825  else
2826  wind->new_securities = updates;
2827  scm_gc_protect_object (wind->new_securities);
2828 
2829  return TRUE;
2830  }
2831 
2832  if (wind->new_securities != SCM_BOOL_F)
2833  return TRUE;
2834 
2835  return FALSE;
2836 }
2837 
2838 
2839 /********************************************************************
2840  * gnc_ui_qif_import_commodity_notebook_update_combos
2841  *
2842  * Scans all commodity notebook pages to make sure all the name space
2843  * combos are in sync
2844  ********************************************************************/
2845 static void
2846 gnc_ui_qif_import_commodity_notebook_update_combos (QIFImportWindow * wind, gboolean init_combos)
2847 {
2848  GList *pageptr;
2849  GtkWidget *notebook_page;
2850  QIFCommNotebookPage *comm_nb_page;
2851 
2852  for (pageptr = wind->commodity_notebook_pages; pageptr; pageptr = pageptr->next)
2853  {
2854  notebook_page = pageptr->data;
2855  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
2856 
2857  /* Get any entered namespace. */
2858  gchar *ns = gnc_ui_namespace_picker_ns (comm_nb_page->namespace_combo);
2859 
2860  /* Update the namespaces available to select. */
2861  if (!ns || !ns[0])
2862  {
2864  comm_nb_page->namespace_combo,
2865  gnc_commodity_get_namespace (comm_nb_page->commodity),
2866  DIAG_COMM_ALL);
2867 
2868  if(!init_combos)
2869  gtk_entry_set_text (GTK_ENTRY(gtk_bin_get_child (
2870  GTK_BIN(comm_nb_page->namespace_combo))), "");
2871  }
2872  else
2873  gnc_ui_update_namespace_picker (comm_nb_page->namespace_combo, ns, DIAG_COMM_ALL);
2874  g_free (ns);
2875  }
2876 }
2877 
2878 
2879 /********************************************************************
2880  * gnc_ui_qif_import_commodity_all_notebook_pages_complete
2881  *
2882  * Scans all commodity notebook pages for the page_complete flag and
2883  * return TRUE if they are all complete
2884  ********************************************************************/
2885 static gboolean
2886 gnc_ui_qif_import_commodity_all_notebook_pages_complete (QIFImportWindow * wind)
2887 {
2888  GList *pageptr;
2889  GtkWidget *notebook_page;
2890  QIFCommNotebookPage *comm_nb_page;
2891  gboolean pages_complete = TRUE;
2892 
2893  for (pageptr = wind->commodity_notebook_pages; pageptr; pageptr = pageptr->next)
2894  {
2895  notebook_page = pageptr->data;
2896  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
2897 
2898  if (!comm_nb_page->page_complete)
2899  pages_complete = FALSE;
2900  }
2901  return pages_complete;
2902 }
2903 
2904 
2905 /********************************************************************
2906 + * gnc_ui_qif_import_commodity_prepare
2907  ********************************************************************/
2908 void
2909 gnc_ui_qif_import_commodity_prepare (GtkAssistant *assistant, gpointer user_data)
2910 {
2911  QIFImportWindow *wind = user_data;
2912 
2913  /* Enable the Assistant "Next" Button */
2914  mark_page_complete (assistant,
2915  gnc_ui_qif_import_commodity_all_notebook_pages_complete (wind));
2916 
2917  /* If there are new securities, prepare the security pages. */
2918  if (wind->new_securities != SCM_BOOL_F)
2919  {
2920  wind->timeout_id = 0;
2921 
2922  /* add the commodity notebook pages */
2923  prepare_security_pages (wind);
2924 
2925  /* make sure all the namespace combos are in sync */
2926  gnc_ui_qif_import_commodity_notebook_update_combos (wind, TRUE);
2927  }
2928 }
2929 
2930 /********************************************************************
2931  * gnc_ui_qif_import_skip_commodity
2932  *
2933  * Determine if we need the import commodity page
2934  ********************************************************************/
2935 static gboolean
2936 gnc_ui_qif_import_skip_commodity (QIFImportWindow *wind)
2937 {
2938  return !gnc_ui_qif_import_new_securities (wind);
2939 }
2940 
2941 
2942 
2943 /*********************************
2944  * gnc_ui_qif_import_comm_valid
2945  ********************************/
2946 static gboolean
2947 gnc_ui_qif_import_comm_valid (GtkAssistant *assistant, gpointer user_data)
2948 {
2949  QIFImportWindow * wind = user_data;
2950  gint num = gtk_notebook_get_current_page (GTK_NOTEBOOK(wind->commodity_notebook));
2951  GtkWidget * notebook_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK(wind->commodity_notebook), num);
2952  QIFCommNotebookPage * comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
2953 
2954  QofBook *book;
2955  gnc_commodity_table *table;
2956  gnc_commodity_namespace *newns;
2957 
2958  gchar *name_space = gnc_ui_namespace_picker_ns (comm_nb_page->namespace_combo);
2959  const gchar *name = gtk_entry_get_text (GTK_ENTRY(comm_nb_page->name_entry));
2960  const gchar *mnemonic = gtk_entry_get_text (GTK_ENTRY(comm_nb_page->mnemonic_entry));
2961 
2962  /* set the page complete flag to TRUE to start with */
2963  comm_nb_page->page_complete = TRUE;
2964 
2965  if (!name || (name[0] == 0))
2966  {
2967  comm_nb_page->page_complete = FALSE;
2968  g_free (name_space);
2969  return FALSE;
2970  }
2971  else if (!mnemonic || (mnemonic[0] == 0))
2972  {
2973  comm_nb_page->page_complete = FALSE;
2974  g_free (name_space);
2975  return FALSE;
2976  }
2977  else if (!name_space || (name_space[0] == 0))
2978  {
2979  comm_nb_page->page_complete = FALSE;
2980  if (name_space)
2981  g_free (name_space);
2982  return FALSE;
2983  }
2984 
2985  /* FIXME: Should check whether a commodity with this namespace and
2986  * mnemonic already exists. If so, ask the user whether to use
2987  * the existing one, or go back and change what they've entered.
2988  */
2989 
2990  book = gnc_get_current_book ();
2992  if (gnc_commodity_namespace_is_iso (name_space) &&
2993  !gnc_commodity_table_lookup (table, name_space, mnemonic))
2994  {
2995  gnc_warning_dialog (GTK_WINDOW(assistant), "%s",
2996  _("You must enter an existing national "
2997  "currency or enter a different type."));
2998 
2999  comm_nb_page->page_complete = FALSE;
3000  g_free (name_space);
3001  return FALSE;
3002  }
3003 
3004  /* Is the namespace a new one? */
3005  if (!gnc_commodity_table_has_namespace (table, name_space))
3006  {
3007  /* Register it so that it will appear as an option on other pages. */
3008  newns = gnc_commodity_table_add_namespace (table, name_space, book);
3009 
3010  /* Remember it so it can be removed if the import gets canceled. */
3011  if (newns)
3012  wind->new_namespaces = g_list_prepend (wind->new_namespaces, name_space);
3013  else
3014  {
3015  g_warning ("QIF import: Couldn't create namespace %s", name_space);
3016  g_free (name_space);
3017  }
3018  }
3019  else
3020  g_free (name_space);
3021 
3022  /* make sure all the namespace combos are in sync */
3023  gnc_ui_qif_import_commodity_notebook_update_combos (wind, FALSE);
3024 
3025  return gnc_ui_qif_import_commodity_all_notebook_pages_complete (wind);
3026 }
3027 
3028 
3029 /*************************************
3030  * gnc_ui_qif_import_comm_changed_cb
3031  ************************************/
3032 void
3033 gnc_ui_qif_import_comm_changed_cb (GtkWidget *widget, gpointer user_data)
3034 {
3035  QIFImportWindow *wind = user_data;
3036  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
3037 
3038  mark_page_complete (assistant,
3039  gnc_ui_qif_import_comm_valid (assistant, user_data));
3040 }
3041 
3042 
3043 static gboolean
3044 do_page_check (gpointer user_data)
3045 {
3046  QIFImportWindow *wind = user_data;
3047  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
3048 
3049  mark_page_complete (assistant,
3050  gnc_ui_qif_import_comm_valid (assistant, wind));
3051 
3052  wind->timeout_id = 0;
3053  return FALSE;
3054 }
3055 
3056 
3057 /**********************************************
3058  * gnc_ui_qif_import_comm_namespace_changed_cb
3059  **********************************************/
3060 void
3061 gnc_ui_qif_import_comm_namespace_changed_cb (GtkWidget *widget, gpointer user_data)
3062 {
3063  QIFImportWindow *wind = user_data;
3064 
3065  if (wind->timeout_id)
3066  g_source_remove (wind->timeout_id);
3067 
3068  /* delay the page check while typing in the combo entry, this should
3069  * reduce the number of false entries as new name spaces are typed, its
3070  * not really a problem as name spaces created but not used do not get imported. */
3071  wind->timeout_id = g_timeout_add (500, (GSourceFunc)do_page_check, wind);
3072 }
3073 
3074 
3075 /**********************************************
3076  * Page 14 - Convert progress Page Procedures
3077  *********************************************/
3078 
3079 /********************************************************************
3080  * gnc_ui_qif_import_convert_progress_pause_cb
3081  *
3082  * Invoked when the "Pause" button is clicked.
3083  ********************************************************************/
3084 void
3085 gnc_ui_qif_import_convert_progress_pause_cb (GtkButton * button,
3086  gpointer user_data)
3087 {
3088  QIFImportWindow *wind = user_data;
3089  SCM toggle_pause = scm_c_eval_string ("qif-import:toggle-pause");
3090  SCM progress;
3091 
3092  if (!wind->busy)
3093  return;
3094 
3095  /* Create SCM for the progress helper. */
3096  progress = SWIG_NewPointerObj (wind->convert_progress,
3097  SWIG_TypeQuery ("_p__GNCProgressDialog"),
3098  0);
3099 
3100  /* Pause (or resume) the currently running operation. */
3101  scm_call_1 (toggle_pause, progress);
3102 
3103  /* Swap the button label between pause and resume. */
3104  if (strcmp (gtk_button_get_label (button), _("_Resume")))
3105  {
3106  gtk_button_set_use_underline (button, TRUE);
3107  gtk_button_set_label (button, _("_Resume"));
3108  }
3109  else
3110  {
3111  gtk_button_set_use_underline (button, FALSE);
3112  gtk_button_set_label (button, _("P_ause"));
3113  }
3114 }
3115 
3116 
3117 /********************************************************************
3118  * gnc_ui_qif_import_convert_progress_start_cb
3119  *
3120  * Invoked when the "Start" button is clicked.
3121  ********************************************************************/
3122 void
3123 gnc_ui_qif_import_convert_progress_start_cb (GtkButton * button,
3124  gpointer user_data)
3125 {
3126  QIFImportWindow *wind = user_data;
3127  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
3128 
3129  SCM qif_to_gnc = scm_c_eval_string ("qif-import:qif-to-gnc");
3130  SCM find_duplicates = scm_c_eval_string ("gnc:account-tree-find-duplicates");
3131  SCM retval;
3132 
3133  /* SCM for the progress dialog. */
3134  SCM progress = SWIG_NewPointerObj (wind->convert_progress,
3135  SWIG_TypeQuery ("_p__GNCProgressDialog"),
3136  0);
3137 
3138  /* The default currency. */
3139  const gchar *currname = gtk_entry_get_text (GTK_ENTRY(gtk_bin_get_child (
3140  GTK_BIN(GTK_COMBO_BOX(wind->currency_picker)))));
3141 
3142  /* Raise the busy flag so the assistant can't be canceled unexpectedly. */
3143  wind->busy = TRUE;
3144  gtk_widget_set_sensitive (wind->convert_pause, TRUE);
3145  gtk_widget_set_sensitive (wind->convert_start, FALSE);
3146 
3147  /* Clear any previous pause or cancel state. */
3148  scm_c_eval_string ("(qif-import:reset-cancel-pause)");
3149 
3150  /* Update the commodities. */
3151  gnc_ui_qif_import_commodity_update (wind);
3152 
3153  /*
3154  * Convert the QIF data into GnuCash data.
3155  *
3156  * A Scheme function does all the work. The return value is the
3157  * root account of an account tree containing all the new accounts
3158  * and transactions. Upon failure, #f is returned. If the user
3159  * cancels, #t is returned.
3160  */
3161 
3162  /* This step will fill 70% of the bar. */
3163  gnc_progress_dialog_push (wind->convert_progress, 0.7);
3164  retval = scm_apply (qif_to_gnc,
3165  scm_list_n (wind->imported_files,
3166  wind->acct_map_info,
3167  wind->cat_map_info,
3168  wind->memo_map_info,
3169  wind->security_hash,
3170  scm_from_utf8_string (currname ? currname : ""),
3171  wind->transaction_status,
3172  progress,
3173  SCM_UNDEFINED),
3174  SCM_EOL);
3175  gnc_progress_dialog_pop (wind->convert_progress);
3176 
3177  if (retval == SCM_BOOL_T)
3178  {
3179  /* Canceled by the user. */
3180 
3181  /* Disable the pause button. */
3182  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3183 
3184  /* Remove any converted data. */
3185  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3186  gnc_ui_qif_import_convert_undo (wind);
3187 
3188  /* Inform the user. */
3189  gnc_progress_dialog_set_sub (wind->convert_progress, _("Canceled"));
3190  gnc_progress_dialog_reset_value (wind->convert_progress);
3191 
3192  wind->busy = FALSE;
3193  wind->load_stop = TRUE;
3194  }
3195  else if (retval == SCM_BOOL_F)
3196  {
3197  /* An bug was encountered during conversion. */
3198 
3199  /* Disable the pause button. */
3200  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3201 
3202  /* Remove any converted data. */
3203  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3204  gnc_ui_qif_import_convert_undo (wind);
3205 
3206  /* Inform the user. */
3207  gnc_progress_dialog_append_log (wind->convert_progress,
3208  _("A bug was detected while converting the QIF data."));
3209  gnc_progress_dialog_set_sub (wind->convert_progress, _("Failed"));
3210  gnc_progress_dialog_reset_value (wind->convert_progress);
3211  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
3212  _("A bug was detected while converting the QIF data."));
3213  /* FIXME: How should we request that the user report this problem? */
3214 
3215  wind->busy = FALSE;
3216  wind->load_stop = TRUE;
3217  }
3218  else if (scm_is_symbol (retval))
3219  {
3220  /* An error was encountered during conversion. */
3221 
3222  /* Disable the pause button. */
3223  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3224 
3225  /* Remove any converted data. */
3226  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3227  gnc_ui_qif_import_convert_undo (wind);
3228 
3229  /* Inform the user. */
3230  gnc_progress_dialog_set_sub (wind->convert_progress, _("Failed"));
3231  gnc_progress_dialog_reset_value (wind->convert_progress);
3232 
3233  wind->busy = FALSE;
3234  wind->load_stop = TRUE;
3235  }
3236  if (wind->load_stop == FALSE)
3237  {
3238  /* Save the imported account tree. */
3239  scm_gc_unprotect_object (wind->imported_account_tree);
3240  wind->imported_account_tree = retval;
3241  scm_gc_protect_object (wind->imported_account_tree);
3242 
3243  /*
3244  * Detect potentially duplicated transactions.
3245  */
3246 
3247  /* This step will fill the remainder of the bar. */
3248  gnc_progress_dialog_push (wind->convert_progress, 1);
3249  retval = scm_call_3 (find_duplicates,
3250  scm_c_eval_string ("(gnc-get-current-root-account)"),
3251  wind->imported_account_tree, progress);
3252  gnc_progress_dialog_pop (wind->convert_progress);
3253 
3254  /* Save the results. */
3255  scm_gc_unprotect_object (wind->match_transactions);
3256  wind->match_transactions = retval;
3257  scm_gc_protect_object (wind->match_transactions);
3258 
3259  if (retval == SCM_BOOL_T)
3260  {
3261  /* Canceled by the user. */
3262  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3263  gnc_progress_dialog_set_sub (wind->convert_progress, _("Canceling"));
3264  wind->busy = FALSE;
3265  wind->load_stop = TRUE;
3266  }
3267  else if (retval == SCM_BOOL_F)
3268  {
3269  /* An error occurred during duplicate checking. */
3270 
3271  /* Remove any converted data. */
3272  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3273  gnc_ui_qif_import_convert_undo (wind);
3274 
3275  /* Inform the user. */
3276  gnc_progress_dialog_append_log (wind->convert_progress,
3277  _("A bug was detected while detecting duplicates."));
3278  gnc_progress_dialog_set_sub (wind->convert_progress, _("Failed"));
3279  gnc_progress_dialog_reset_value (wind->convert_progress);
3280  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
3281  _("A bug was detected while detecting duplicates."));
3282  /* FIXME: How should we request that the user report this problem? */
3283 
3284  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3285  wind->busy = FALSE;
3286  wind->load_stop = TRUE;
3287  }
3288  }
3289  /* Enable the Assistant "Next" Button */
3290  mark_page_complete (assistant, TRUE);
3291 
3292  /* Set Pause and Start buttons */
3293  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3294  gtk_widget_set_sensitive (wind->convert_start, FALSE);
3295 
3296  if (wind->load_stop == FALSE)
3297  {
3298  /* The conversion completed successfully. */
3299  gnc_progress_dialog_set_sub (wind->convert_progress,
3300  _("Conversion completed"));
3301  gnc_progress_dialog_set_value (wind->convert_progress, 1);
3302 
3303  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3304  wind->busy = FALSE;
3305 
3306  /* If the log is empty, move on to the next page automatically. */
3307  if (gtk_text_buffer_get_char_count (gtk_text_view_get_buffer (GTK_TEXT_VIEW(wind->convert_log))) == 0) {
3308  gtk_assistant_next_page (assistant);
3309  }
3310  }
3311 }
3312 
3313 
3314 /********************************************************************
3315  * gnc_ui_qif_import_convert_progress_prepare
3316  *
3317  * Prepare the data conversion progress page for display.
3318  ********************************************************************/
3319 void
3320 gnc_ui_qif_import_convert_progress_prepare (GtkAssistant *assistant,
3321  gpointer user_data)
3322 {
3323  QIFImportWindow *wind = user_data;
3324 
3325  /* Reset the progress display. */
3326  gnc_progress_dialog_set_primary (wind->convert_progress, "");
3327  gnc_progress_dialog_set_secondary (wind->convert_progress,
3328  _("When you press the Start Button, GnuCash will import your QIF data. "
3329  "If there are no errors or warnings, you will automatically proceed to "
3330  "the next step. Otherwise, the details will be shown below for your review."));
3331  gnc_progress_dialog_set_sub (wind->convert_progress, " ");
3332  gnc_progress_dialog_reset_value (wind->convert_progress);
3333  gnc_progress_dialog_reset_log (wind->convert_progress);
3334 
3335  /* Set Pause and Start buttons */
3336  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3337  gtk_widget_set_sensitive (wind->convert_start, TRUE);
3338 
3339  /* Disable the assistant "Next" button */
3340  mark_page_complete (assistant, FALSE);
3341 
3342  /* Before creating transactions, if this is a new book, let user specify
3343  * book options, since they affect how transactions are created */
3344  if (wind->new_book)
3345  wind->new_book = gnc_new_book_option_display (wind->window);
3346 }
3347 
3348 
3349 /*****************************************
3350  * Page 15 - Match Doc. Page Procedures
3351  *****************************************/
3352 
3353 /********************************************************************
3354  * gnc_ui_qif_import_duplicates_doc_prepare
3355  ********************************************************************/
3356 void
3357 gnc_ui_qif_import_duplicates_doc_prepare (GtkAssistant *assistant,
3358  gpointer user_data)
3359 {
3360  /* Enable the Assistant "Next" Button */
3361  mark_page_complete (assistant, TRUE);
3362 
3363 }
3364 
3365 /********************************************************************
3366  * gnc_ui_qif_import_skip_duplicates_doc
3367  *
3368  * Determine if we need the import duplicates doc page
3369  ********************************************************************/
3370 static gboolean
3371 gnc_ui_qif_import_skip_duplicates_doc (QIFImportWindow *wind)
3372 {
3373  /* Jump over doc page if show_doc_pages FALSE */
3374  if (!wind->show_doc_pages)
3375  return TRUE;
3376 
3377  /* Don't show doc page if there are no duplicates */
3378  if (scm_is_null (wind->match_transactions))
3379  return TRUE;
3380 
3381  return FALSE;
3382 }
3383 
3384 /**********************************************
3385  * Page 16 - Match Duplicates Page Procedures
3386  **********************************************/
3387 
3388 /********************************************************************
3389  * gnc_ui_qif_import_duplicates_match_prepare
3390  ********************************************************************/
3391 void
3392 gnc_ui_qif_import_duplicates_match_prepare (GtkAssistant *assistant,
3393  gpointer user_data)
3394 {
3395  QIFImportWindow * wind = user_data;
3396 
3397  GtkTreeView *view;
3398  GtkListStore *store;
3399  SCM duplicates;
3400  SCM current_xtn;
3401  Transaction *gnc_xtn;
3402  Split *gnc_split;
3403  GtkTreeIter iter;
3404  GtkTreeSelection *selection;
3405  GtkTreePath *path;
3406  const gchar *amount_str;
3407  int rownum = 0;
3408 
3409  if (!scm_is_null (wind->match_transactions))
3410  {
3411  view = GTK_TREE_VIEW(wind->new_transaction_view);
3412  store = GTK_LIST_STORE(gtk_tree_view_get_model (view));
3413  gtk_list_store_clear (store);
3414 
3415  if (!scm_is_list (wind->match_transactions))
3416  return;
3417 
3418  /* Loop through the list of new, potentially duplicate transactions. */
3419  duplicates = wind->match_transactions;
3420  while (!scm_is_null (duplicates))
3421  {
3422  gdouble amount_gd = 0;
3423  time64 send_time = 0;
3424  char datebuff [MAX_DATE_LENGTH + 1];
3425  memset (datebuff, 0, MAX_DATE_LENGTH);
3426  current_xtn = SCM_CAAR(duplicates);
3427 #define FUNC_NAME "xaccTransCountSplits"
3428  gnc_xtn = SWIG_MustGetPtr (current_xtn,
3429  SWIG_TypeQuery ("_p_Transaction"), 1, 0);
3430 #undef FUNC_NAME
3431  if (xaccTransCountSplits (gnc_xtn) > 2)
3432  amount_str = _("(split)");
3433  else
3434  {
3435  gnc_split = xaccTransGetSplit (gnc_xtn, 0);
3436  amount_str =
3438  gnc_account_print_info
3439  (xaccSplitGetAccount (gnc_split), TRUE));
3440  amount_gd = gnc_numeric_to_double (xaccSplitGetValue(gnc_split));
3441  }
3442  gtk_list_store_append (store, &iter);
3443  send_time = xaccTransRetDatePosted (gnc_xtn);
3444  qof_print_date_buff (datebuff, MAX_DATE_LENGTH, send_time);
3445  gtk_list_store_set
3446  (store, &iter,
3447  QIF_TRANS_COL_INDEX, rownum++,
3448  QIF_TRANS_COL_DATE, datebuff,
3449  QIF_TRANS_COL_DATE_INT64, send_time, // used for sorting
3450  QIF_TRANS_COL_DESCRIPTION, xaccTransGetDescription (gnc_xtn),
3451  QIF_TRANS_COL_AMOUNT, amount_str,
3452  QIF_TRANS_COL_AMOUNT_DOUBLE, amount_gd, // used for sorting
3453  -1);
3454 
3455  duplicates = SCM_CDR(duplicates);
3456  }
3457  selection = gtk_tree_view_get_selection (view);
3458  path = gtk_tree_path_new_from_indices (0, -1);
3459  gtk_tree_selection_select_path (selection, path);
3460  gtk_tree_path_free (path);
3461  }
3462 
3463  /* Enable the Assistant "Next" Button */
3464  mark_page_complete (assistant, TRUE);
3465 }
3466 
3467 /********************************************************************
3468  * gnc_ui_qif_import_skip_duplicates_match
3469  *
3470  * Determine if we need the import duplicates match page
3471  ********************************************************************/
3472 static gboolean
3473 gnc_ui_qif_import_skip_duplicates_match (QIFImportWindow *wind)
3474 {
3475  /* Don't show page if there are no duplicates */
3476  return scm_is_null (wind->match_transactions);
3477 }
3478 
3479 
3480 /*************************************
3481  * Page 17 - Apply Page Procedures
3482  *************************************/
3483 
3484 /********************************************************************
3485  * gnc_ui_qif_import_end_page_prepare
3486  ********************************************************************/
3487 void
3488 gnc_ui_qif_import_end_page_prepare (GtkAssistant *assistant,
3489  gpointer user_data)
3490 {
3491  /* Enable the Assistant "Next" Button */
3492  mark_page_complete (assistant, TRUE);
3493 }
3494 
3495 
3496 /********************************************************************
3497  * gnc_ui_qif_import_finish_cb
3498  *
3499  * Invoked when the "Apply" button is clicked on the final page.
3500  ********************************************************************/
3501 void
3502 gnc_ui_qif_import_finish_cb (GtkAssistant *assistant,
3503  gpointer user_data)
3504 {
3505  QIFImportWindow * wind = user_data;
3506 
3507  SCM save_map_prefs = scm_c_eval_string ("qif-import:save-map-prefs");
3508  SCM cat_and_merge = scm_c_eval_string ("gnc:account-tree-catenate-and-merge");
3509  SCM prune_xtns = scm_c_eval_string ("gnc:prune-matching-transactions");
3510  SCM scm_result;
3511 
3512  GncPluginPage *page;
3513  gboolean acct_tree_found = FALSE;
3514 
3515  gnc_suspend_gui_refresh ();
3516 
3517  /* Prune any imported transactions that were determined to be duplicates. */
3518  if (wind->match_transactions != SCM_BOOL_F)
3519  scm_call_1 (prune_xtns, wind->match_transactions);
3520 
3521  /* Merge the imported account tree with the existing one. */
3522  if (wind->imported_account_tree != SCM_BOOL_F)
3523  scm_call_2 (cat_and_merge,
3524  scm_c_eval_string ("(gnc-get-current-root-account)"),
3525  wind->imported_account_tree);
3526 
3527  gnc_resume_gui_refresh ();
3528 
3529  /* Save the user's mapping preferences. */
3530  scm_result = scm_apply (save_map_prefs,
3531  scm_list_5 (wind->acct_map_info, wind->cat_map_info,
3532  wind->memo_map_info, wind->security_hash,
3533  wind->security_prefs),
3534  SCM_EOL);
3535 
3536  if (scm_result == SCM_BOOL_F)
3537  gnc_warning_dialog (GTK_WINDOW(assistant), "%s",
3538  _("GnuCash was unable to save your mapping preferences."));
3539 
3540  /* Open an account tab in the main window if one doesn't exist already. */
3541  gnc_main_window_foreach_page (gnc_ui_qif_import_check_acct_tree,
3542  &acct_tree_found);
3543 
3544  wind->acct_tree_found = acct_tree_found;
3545  if (!acct_tree_found)
3546  {
3548  gnc_main_window_open_page (NULL, page);
3549  }
3550 }
3551 
3552 
3553 /***************************************
3554  * Page 18 - Summary Page Procedures
3555  ***************************************/
3556 
3557 /********************************************************************
3558  * gnc_ui_qif_import_summary_page_prepare
3559  ********************************************************************/
3560 void
3561 gnc_ui_qif_import_summary_page_prepare (GtkAssistant *assistant,
3562  gpointer user_data)
3563 {
3564  QIFImportWindow * wind = user_data;
3565 
3566  const gchar *msg = wind->load_stop ?
3567  _("There was a problem with the import.") :
3568  _("QIF Import Completed.");
3569 
3570  gchar *text = g_markup_printf_escaped ("<span size=\"large\"><b>%s</b></span>", msg);
3571 
3572  gtk_label_set_markup (GTK_LABEL(wind->summary_text), text);
3573 
3574  g_free (text);
3575 
3576  /* Enable the Assistant "Next" Button */
3577  mark_page_complete (assistant, TRUE);
3578 }
3579 
3580 
3581 /********************************************************************
3582  * Prepare callback for assistant pages.
3583  ********************************************************************/
3584 void gnc_ui_qif_import_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
3585  gpointer user_data)
3586 {
3587  gint currentpage = gtk_assistant_get_current_page (assistant);
3588  GtkWidget *mypage = gtk_assistant_get_nth_page (assistant, currentpage);
3589  const char *pagename = gtk_buildable_get_name (GTK_BUILDABLE(mypage));
3590 
3591  ENTER("Page %s", pagename);
3592 
3593  if (!g_strcmp0 (pagename, "start_page"))
3594  {
3595  /* Current page is Intro page */
3596  gnc_ui_qif_import_intro_prepare (assistant, user_data);
3597  }
3598  else if (!g_strcmp0 (pagename, "load_file_page"))
3599  {
3600  /* Current page is File Load */
3601  gnc_ui_qif_import_load_file_prepare (assistant, user_data);
3602  }
3603  else if (!g_strcmp0 (pagename, "load_progress_page"))
3604  {
3605  /* Current page is Load Progress */
3606  gnc_ui_qif_import_load_progress_prepare (assistant, user_data);
3607  }
3608  else if (!g_strcmp0 (pagename, "date_format_page"))
3609  {
3610  /* Current page is date page */
3611  /* No preparation required */
3612  }
3613  else if (!g_strcmp0 (pagename, "account_name_page"))
3614  {
3615  /* Current page is account page */
3616  gnc_ui_qif_import_account_prepare (assistant, user_data);
3617  }
3618  else if (!g_strcmp0 (pagename, "loaded_files_page"))
3619  {
3620  /* Current page is loaded files page */
3621  gnc_ui_qif_import_loaded_files_prepare (assistant, user_data);
3622  }
3623  else if (!g_strcmp0 (pagename, "account_doc_page"))
3624  {
3625  /* Current page is Account Doc. page */
3626  gnc_ui_qif_import_account_doc_prepare (assistant, user_data);
3627  }
3628  else if (!g_strcmp0 (pagename, "account_match_page"))
3629  {
3630  /* Current page is Account Match page */
3631  gnc_ui_qif_import_account_match_prepare (assistant, user_data);
3632  }
3633  else if (!g_strcmp0 (pagename, "category_doc_page"))
3634  {
3635  /* Current page is Category Doc. page */
3636  gnc_ui_qif_import_category_doc_prepare (assistant, user_data);
3637  }
3638  else if (!g_strcmp0 (pagename, "category_match_page"))
3639  {
3640  /* Current page is Category Match page */
3641  gnc_ui_qif_import_category_match_prepare (assistant, user_data);
3642  }
3643  else if (!g_strcmp0 (pagename, "memo_doc_page"))
3644  {
3645  /* Current page is Memo Doc. page */
3646  gnc_ui_qif_import_memo_doc_prepare (assistant, user_data);
3647  }
3648  else if (!g_strcmp0 (pagename, "memo_match_page"))
3649  {
3650  /* Current page is Memo Match page */
3651  gnc_ui_qif_import_memo_match_prepare (assistant, user_data);
3652  }
3653  else if (!g_strcmp0 (pagename, "currency_book_option_page"))
3654  {
3655  /* Current page is Currency page */
3656  gnc_ui_qif_import_currency_prepare (assistant, user_data);
3657  }
3658  else if (!g_strcmp0 (pagename, "commodity_page"))
3659  {
3660  /* Current page is Commodity page */
3661  gnc_ui_qif_import_commodity_prepare (assistant, user_data);
3662  }
3663  else if (!g_strcmp0 (pagename, "convert_progress_page"))
3664  {
3665  /* Current page is Conversion progress page */
3666  gnc_ui_qif_import_convert_progress_prepare (assistant, user_data);
3667  }
3668  else if (!g_strcmp0 (pagename, "duplicates_doc_page"))
3669  {
3670  /* Current page is Duplicates Doc page */
3671  gnc_ui_qif_import_duplicates_doc_prepare (assistant, user_data);
3672  }
3673  else if (!g_strcmp0 (pagename, "duplicates_match_page"))
3674  {
3675  /* Current page is Duplicates Match page */
3676  gnc_ui_qif_import_duplicates_match_prepare (assistant, user_data);
3677  }
3678  else if (!g_strcmp0 (pagename, "end_page"))
3679  {
3680  /* Current page is the end page */
3681  gnc_ui_qif_import_end_page_prepare (assistant, user_data);
3682  }
3683  else if (!g_strcmp0 (pagename, "summary_page"))
3684  {
3685  /* Current page is the summary page */
3686  gnc_ui_qif_import_summary_page_prepare (assistant, user_data);
3687  }
3688  LEAVE("");
3689 }
3690 
3691 
3692 /********************************************************************
3693  * get_assistant_widgets
3694  *
3695  * Get all builder-defined widgets that need to be actively managed.
3696  ********************************************************************/
3697 static void
3698 get_assistant_widgets (QIFImportWindow *wind, GtkBuilder *builder)
3699 {
3700  g_return_if_fail (wind);
3701  g_return_if_fail (builder);
3702 
3703  wind->window = GTK_WIDGET(gtk_builder_get_object (builder, "qif_import_assistant"));
3704  wind->filename_entry = GTK_WIDGET(gtk_builder_get_object (builder, "qif_filename_entry"));
3705  wind->load_pause = GTK_WIDGET(gtk_builder_get_object (builder, "load_progress_pause"));
3706  wind->load_start = GTK_WIDGET(gtk_builder_get_object (builder, "load_progress_start"));
3707  wind->load_log = GTK_WIDGET(gtk_builder_get_object (builder, "load_progress_log"));
3708  wind->load_progress = gnc_progress_dialog_custom (
3709  GTK_LABEL(gtk_builder_get_object (builder, "load_progress_primary")),
3710  GTK_LABEL(gtk_builder_get_object (builder, "load_progress_secondary")),
3711  GTK_PROGRESS_BAR(gtk_builder_get_object (builder, "load_progress_bar")),
3712  GTK_LABEL(gtk_builder_get_object (builder, "load_progress_sub")),
3713  GTK_TEXT_VIEW(wind->load_log));
3714  wind->acct_entry = GTK_WIDGET(gtk_builder_get_object (builder, "qif_account_entry"));
3715  wind->date_format_combo = GTK_WIDGET(gtk_builder_get_object (builder, "date_format_combobox"));
3716  wind->selected_file_view = GTK_WIDGET(gtk_builder_get_object (builder, "selected_file_view"));
3717  wind->unload_file_btn = GTK_WIDGET(gtk_builder_get_object (builder, "unload_file_button"));
3718  wind->currency_picker = GTK_WIDGET(gtk_builder_get_object (builder, "currency_comboboxentry"));
3719  wind->book_option_label = GTK_WIDGET(gtk_builder_get_object (builder, "book_option_label"));
3720  wind->book_option_message = GTK_WIDGET(gtk_builder_get_object (builder, "book_option_message_label"));
3721  wind->commodity_notebook = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_notebook"));
3722  wind->acct_view = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_view"));
3723  wind->acct_view_count = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_count"));
3724  wind->acct_view_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_change"));
3725  wind->cat_view = GTK_WIDGET(gtk_builder_get_object (builder, "category_page_view"));
3726  wind->cat_view_count = GTK_WIDGET(gtk_builder_get_object (builder, "category_page_count"));
3727  wind->cat_view_btn = GTK_WIDGET(gtk_builder_get_object (builder, "category_page_change"));
3728  wind->memo_view = GTK_WIDGET(gtk_builder_get_object (builder, "memo_page_view"));
3729  wind->memo_view_count = GTK_WIDGET(gtk_builder_get_object (builder, "memo_page_count"));
3730  wind->memo_view_btn = GTK_WIDGET(gtk_builder_get_object (builder, "memo_page_change"));
3731  wind->convert_pause = GTK_WIDGET(gtk_builder_get_object (builder, "convert_progress_pause"));
3732  wind->convert_start = GTK_WIDGET(gtk_builder_get_object (builder, "convert_progress_start"));
3733  wind->convert_log = GTK_WIDGET(gtk_builder_get_object (builder, "convert_progress_log"));
3734  wind->convert_progress = gnc_progress_dialog_custom (
3735  GTK_LABEL(gtk_builder_get_object (builder, "convert_progress_primary")),
3736  GTK_LABEL(gtk_builder_get_object (builder, "convert_progress_secondary")),
3737  GTK_PROGRESS_BAR(gtk_builder_get_object (builder, "convert_progress_bar")),
3738  GTK_LABEL(gtk_builder_get_object (builder, "convert_progress_sub")),
3739  GTK_TEXT_VIEW(wind->convert_log));
3740  wind->summary_text = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
3741 
3742  // Set the name for this assistant so it can be easily manipulated with css
3743  gtk_widget_set_name (GTK_WIDGET(wind->window), "gnc-id-assistant-qif-import");
3744  gnc_widget_style_context_add_class (GTK_WIDGET(wind->window), "gnc-class-imports");
3745 
3746  wind->new_transaction_view =
3747  GTK_WIDGET(gtk_builder_get_object (builder, "new_transaction_view"));
3748  wind->old_transaction_view =
3749  GTK_WIDGET(gtk_builder_get_object (builder, "old_transaction_view"));
3750 }
3751 
3752 
3753 /********************************************************************
3754  * build_views
3755  *
3756  * Build the details of all GtkTreeView widgets.
3757  ********************************************************************/
3758 static void
3759 build_views (QIFImportWindow *wind)
3760 {
3761  GtkTreeView *view;
3762  GtkListStore *store;
3763  GtkCellRenderer *renderer;
3764  GtkTreeViewColumn *column;
3765  GtkTreeSelection *selection;
3766 
3767  g_return_if_fail (wind);
3768 
3769  /* Set up the selected file view */
3770  view = GTK_TREE_VIEW(wind->selected_file_view);
3771  store = gtk_list_store_new (NUM_FILENAME_COLS, G_TYPE_INT, G_TYPE_STRING);
3772  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
3773  g_object_unref (store);
3774 
3775  renderer = gtk_cell_renderer_text_new ();
3776  column = gtk_tree_view_column_new_with_attributes ("unused",
3777  renderer,
3778  "text",
3779  FILENAME_COL_NAME,
3780  NULL);
3781  gtk_tree_view_append_column (view, column);
3782 
3783  selection = gtk_tree_view_get_selection (view);
3784  g_signal_connect (selection, "changed",
3785  G_CALLBACK(gnc_ui_qif_import_select_loaded_file_cb),
3786  wind);
3787 
3788  /* Set up the QIF account to GnuCash account matcher. */
3789  create_account_picker_view (wind->acct_view, _("QIF account name"),
3790  G_CALLBACK(gnc_ui_qif_import_account_activate_cb),
3791  G_CALLBACK(gnc_ui_qif_import_account_select_cb),
3792  wind);
3793 
3794  /* Set up the QIF category to GnuCash account matcher. */
3795  create_account_picker_view (wind->cat_view, _("QIF category name"),
3796  G_CALLBACK(gnc_ui_qif_import_category_activate_cb),
3797  G_CALLBACK(gnc_ui_qif_import_category_select_cb),
3798  wind);
3799 
3800  /* Set up the QIF payee/memo to GnuCash account matcher. */
3801  create_account_picker_view (wind->memo_view, _("QIF payee/memo"),
3802  G_CALLBACK(gnc_ui_qif_import_memo_activate_cb),
3803  G_CALLBACK(gnc_ui_qif_import_memo_select_cb),
3804  wind);
3805 
3806  /* Set up the new transaction view */
3807  view = GTK_TREE_VIEW(wind->new_transaction_view);
3808  store = gtk_list_store_new (NUM_QIF_TRANS_COLS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT64,
3809  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_BOOLEAN);
3810  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
3811 
3812  /* default sort order */
3813  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
3814  QIF_TRANS_COL_DATE_INT64,
3815  GTK_SORT_ASCENDING);
3816  g_object_unref (store);
3817 
3818  /* prevent the rows being dragged to a different order */
3819  gtk_tree_view_set_reorderable (view, FALSE);
3820 
3821  renderer = gtk_cell_renderer_text_new ();
3822  column = gtk_tree_view_column_new_with_attributes (_("Date"),
3823  renderer,
3824  "text",
3825  QIF_TRANS_COL_DATE,
3826  NULL);
3827  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3828  gtk_tree_view_append_column (view, column);
3829  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DATE_INT64);
3830 
3831  renderer = gtk_cell_renderer_text_new ();
3832  column = gtk_tree_view_column_new_with_attributes (_("Description"),
3833  renderer,
3834  "text",
3835  QIF_TRANS_COL_DESCRIPTION,
3836  NULL);
3837  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3838  gtk_tree_view_append_column (view, column);
3839  gtk_tree_view_column_set_expand(column, TRUE);
3840  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DESCRIPTION);
3841 
3842  renderer = gtk_cell_renderer_text_new ();
3843  column = gtk_tree_view_column_new_with_attributes (_("Amount"),
3844  renderer,
3845  "text",
3846  QIF_TRANS_COL_AMOUNT,
3847  NULL);
3848  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3849  gtk_tree_view_append_column (view, column);
3850  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_AMOUNT_DOUBLE);
3851 
3852  selection = gtk_tree_view_get_selection (view);
3853  g_signal_connect (selection, "changed",
3854  G_CALLBACK(gnc_ui_qif_import_duplicate_new_select_cb),
3855  wind);
3856 
3857  /* Set up the old transaction view */
3858  view = GTK_TREE_VIEW(wind->old_transaction_view);
3859  store = gtk_list_store_new (NUM_QIF_TRANS_COLS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT64,
3860  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_BOOLEAN);
3861  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
3862 
3863  /* default sort order */
3864  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
3865  QIF_TRANS_COL_DATE_INT64,
3866  GTK_SORT_ASCENDING);
3867  g_object_unref (store);
3868 
3869  /* prevent the rows being dragged to a different order */
3870  gtk_tree_view_set_reorderable (view, FALSE);
3871 
3872  renderer = gtk_cell_renderer_text_new ();
3873  column = gtk_tree_view_column_new_with_attributes (_("Date"),
3874  renderer,
3875  "text",
3876  QIF_TRANS_COL_DATE,
3877  NULL);
3878  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3879  gtk_tree_view_append_column (view, column);
3880  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DATE_INT64);
3881 
3882  renderer = gtk_cell_renderer_text_new ();
3883  column = gtk_tree_view_column_new_with_attributes (_("Description"),
3884  renderer,
3885  "text",
3886  QIF_TRANS_COL_DESCRIPTION,
3887  NULL);
3888  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3889  gtk_tree_view_append_column (view, column);
3890  gtk_tree_view_column_set_expand (column, TRUE);
3891  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DESCRIPTION);
3892 
3893  renderer = gtk_cell_renderer_text_new ();
3894  column = gtk_tree_view_column_new_with_attributes (_("Amount"),
3895  renderer,
3896  "text",
3897  QIF_TRANS_COL_AMOUNT,
3898  NULL);
3899  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3900  gtk_tree_view_append_column (view, column);
3901  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_AMOUNT_DOUBLE);
3902 
3903  renderer = gtk_cell_renderer_toggle_new ();
3904  column = gtk_tree_view_column_new_with_attributes (_("Match?"),
3905  renderer,
3906  "active",
3907  QIF_TRANS_COL_CHECKED,
3908  NULL);
3909  gtk_tree_view_append_column (view, column);
3910 
3911  selection = gtk_tree_view_get_selection (view);
3912  g_signal_connect (selection, "changed",
3913  G_CALLBACK(gnc_ui_qif_import_duplicate_old_select_cb),
3914  wind);
3915 }
3916 
3917 
3918 /********************************************************************
3919  * gnc_ui_qif_import_assistant_make
3920  *
3921  * Build a new QIF import assistant.
3922  ********************************************************************/
3923 static GtkWidget *
3924 gnc_ui_qif_import_assistant_make (QIFImportWindow *qif_win)
3925 {
3926  GtkBuilder *builder;
3927  GtkWidget *box;
3928 
3929  builder = gtk_builder_new ();
3930  gnc_builder_add_from_file (builder, "assistant-qif-import.glade", "date_format_liststore");
3931  gnc_builder_add_from_file (builder, "assistant-qif-import.glade", "qif_import_assistant");
3932 
3933  qif_win->new_namespaces = NULL;
3934  qif_win->selected_transaction = 0;
3935  qif_win->busy = FALSE;
3936  /* In order to include a book options display on the creation of a new book,
3937  * we need to detect when we are dealing with a new book. */
3938  qif_win->new_book = gnc_is_new_book ();
3939 
3940  /* Get all user preferences related to QIF importing. */
3941  get_preferences (qif_win);
3942 
3943  /* Set up the Scheme side of things. Note that if a session/book did not
3944  * exist prior to this function, it is created within scheme function
3945  * "qif-import:load-map-prefs", so we need to have set the flag previously */
3946  initialize_scheme (qif_win);
3947 
3948  /* Get all interesting builder-defined widgets. */
3949  get_assistant_widgets (qif_win, builder);
3950  GtkAssistant *assistant = GTK_ASSISTANT(qif_win->window);
3951 
3952  /* Make this window stay on top */
3953  gtk_window_set_transient_for (GTK_WINDOW(qif_win->window), gnc_ui_get_main_window (NULL));
3954 
3955  /* Build the details of all GtkTreeView widgets. */
3956  build_views (qif_win);
3957  PINFO ("Total Number of Assistant Pages is %d", gtk_assistant_get_n_pages (assistant));
3958 
3959  /* Establish a custom next page function. */
3960  gtk_assistant_set_forward_page_func(assistant,
3961  gnc_ui_qif_import_assistant_page_forward, qif_win, NULL);
3962 
3963  /* Currency Page */
3964  /* Set a default currency for new accounts */
3965  qif_win->currency_picker = gnc_currency_edit_new ();
3966  gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(qif_win->currency_picker), gnc_default_currency ());
3967  gtk_widget_show (qif_win->currency_picker);
3968  box = GTK_WIDGET(gtk_builder_get_object (builder, "currency_picker_hbox"));
3969  gtk_box_pack_start (GTK_BOX(box), qif_win->currency_picker, TRUE, TRUE, 0);
3970 
3971  gnc_restore_window_size (GNC_PREFS_GROUP,
3972  GTK_WINDOW(qif_win->window), gnc_ui_get_main_window (NULL));
3973 
3974  g_signal_connect (qif_win->window, "destroy",
3975  G_CALLBACK(gnc_ui_qif_import_assistant_destroy), qif_win);
3976 
3977  gtk_builder_connect_signals (builder, qif_win);
3978 
3979  g_object_unref (G_OBJECT(builder));
3980 
3981  gtk_widget_show_all (qif_win->window);
3982  gtk_window_present (GTK_WINDOW(qif_win->window));
3983 
3984  return qif_win->window;
3985 }
3986 
3987 
3988 /********************************************
3989  * gnc_ui_qif_import_assistant_close_handler
3990  ********************************************/
3991 static void
3992 gnc_ui_qif_import_assistant_close_handler (gpointer user_data)
3993 {
3994  QIFImportWindow *qif_win = user_data;
3995 
3996  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(qif_win->window));
3997  gtk_widget_destroy (qif_win->window);
3998 }
3999 
4000 
4001 /********************************************
4002  * gnc_file_qif_import
4003  ********************************************/
4004 void
4005 gnc_file_qif_import (void)
4006 {
4007  QIFImportWindow *qif_win;
4008  gint component_id;
4009  SCM has_regex = scm_c_eval_string ("(defined? 'make-regexp)");
4010 
4011  if (scm_is_false(has_regex) == 1)
4012  {
4013  gnc_warning_dialog(NULL, _("QIF import requires guile with regex support."));
4014  return;
4015  }
4016 
4017  qif_win = g_new0 (QIFImportWindow, 1);
4018 
4019  /* pop up the QIF File Import dialog box */
4020  gnc_ui_qif_import_assistant_make (qif_win);
4021 
4022  component_id = gnc_register_gui_component (ASSISTANT_QIF_IMPORT_CM_CLASS,
4023  NULL, gnc_ui_qif_import_assistant_close_handler,
4024  qif_win);
4025 
4026  gnc_gui_component_watch_entity_type (component_id,
4027  GNC_ID_ACCOUNT,
4028  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
4029 
4030  gtk_widget_show_all (qif_win->window);
4031 
4032  gnc_window_adjust_for_screen (GTK_WINDOW(qif_win->window));
4033 }
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction&#39;s split list.
The instance data structure for a content plugin.
void gnc_currency_edit_set_currency(GNCCurrencyEdit *gce, const gnc_commodity *currency)
Set the widget to display a certain currency name.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
GncPluginPage * gnc_plugin_page_account_tree_new(void)
Create a new "account tree" plugin page.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
void gnc_progress_dialog_set_secondary(GNCProgressDialog *progress, const gchar *str)
Set the secondary text of the progress dialog.
void gnc_progress_dialog_append_log(GNCProgressDialog *progress, const gchar *str)
Append str to the progress log.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gtk helper routines.
gchar * gnc_ui_namespace_picker_ns(GtkWidget *cbwe)
Given a combo box, return the currently selected namespaces.
void gnc_progress_dialog_destroy(GNCProgressDialog *progress)
Destroy the dialog.
void gnc_progress_dialog_set_value(GNCProgressDialog *progress, gdouble value)
Set the fraction of the progress bar to fill, where 0 is empty and 1 is full.
API for displaying progress of long-running operations.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
void gnc_progress_dialog_reset_value(GNCProgressDialog *progress)
Pop up to the top level and clear the progress bar.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
void gnc_main_window_foreach_page(GncMainWindowPageFunc fn, gpointer user_data)
Iterator function to walk all pages in all windows, calling the specified function for each page...
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GtkWindow * gnc_ui_get_gtk_window(GtkWidget *widget)
Get a pointer to the widget&#39;s immediate top level GtkWindow.
gnc_commodity_namespace * gnc_commodity_table_add_namespace(gnc_commodity_table *table, const char *name_space, QofBook *book)
This function adds a new string to the list of commodity namespaces.
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
Functions for adding content to a window.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Dialog box should allow selection of anything.
GNCProgressDialog * gnc_progress_dialog_custom(GtkLabel *primary, GtkLabel *secondary, GtkProgressBar *bar, GtkLabel *suboperation, GtkTextView *log)
Creates a dialog for displaying the progress of an activity using existing widgets.
int xaccTransCountSplits(const Transaction *trans)
Returns the number of splits in this transaction.
Currency selection widget.
gdouble gnc_numeric_to_double(gnc_numeric n)
Convert numeric to floating-point value.
Account handling public routines.
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
void gnc_progress_dialog_set_sub(GNCProgressDialog *progress, const gchar *str)
Set the suboperation text of the progress dialog.
int gnc_commodity_table_has_namespace(const gnc_commodity_table *table, const char *name_space)
Test to see if the indicated namespace exits in the commodity table.
void gnc_commodity_table_delete_namespace(gnc_commodity_table *table, const char *name_space)
This function deletes a string from the list of commodity namespaces.
gboolean gnc_commodity_namespace_is_iso(const char *name_space)
Checks to see if the specified commodity namespace is the namespace for ISO 4217 currencies.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
void gnc_progress_dialog_reset_log(GNCProgressDialog *progress)
Show the progress log and delete any existing text.
void gnc_progress_dialog_set_primary(GNCProgressDialog *progress, const gchar *str)
Set the primary text of the progress dialog.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
void qof_book_mark_session_dirty(QofBook *book)
The qof_book_mark_dirty() routine marks the book as having been modified.
Definition: qofbook.cpp:397
Generic api to store and retrieve preferences.
Functions providing a chart of account page.
void gnc_commodity_set_fullname(gnc_commodity *cm, const char *fullname)
Set the full name for the specified commodity.
GtkWidget * gnc_currency_edit_new(void)
Create a new GNCCurrencyEdit widget which can be used to provide an easy way to enter ISO currency co...
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
void gnc_commodity_set_mnemonic(gnc_commodity *cm, const char *mnemonic)
Set the mnemonic for the specified commodity.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
void gnc_commodity_set_namespace(gnc_commodity *cm, const char *name_space)
Set the namespace for the specified commodity.
This file contains the functions to present a GUI to select a file or a database connection.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
guint gnc_progress_dialog_pop(GNCProgressDialog *progress)
Moves up one level in the stack of virtual bars.
API for Transactions and Splits (journal entries)
void gnc_ui_update_namespace_picker(GtkWidget *cbwe, const gchar *sel, dialog_commodity_mode mode)
Given a combo box, fill in the known commodity namespaces and then select one.
"select" and "new" commodity windows
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:574
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
guint gnc_progress_dialog_push(GNCProgressDialog *progress, gdouble weight)
Create a new "virtual" progress bar that, as it becomes full, will fill the current bar by the fracti...