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  const gchar *ns;
2855 
2856  notebook_page = pageptr->data;
2857  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
2858 
2859  /* Get any entered namespace. */
2860  ns = gnc_ui_namespace_picker_ns (comm_nb_page->namespace_combo);
2861 
2862  /* Update the namespaces available to select. */
2863  if (!ns || !ns[0])
2864  {
2866  comm_nb_page->namespace_combo,
2867  gnc_commodity_get_namespace (comm_nb_page->commodity),
2868  DIAG_COMM_ALL);
2869 
2870  if(!init_combos)
2871  gtk_entry_set_text (GTK_ENTRY(gtk_bin_get_child (
2872  GTK_BIN(comm_nb_page->namespace_combo))), "");
2873  }
2874  else
2875  gnc_ui_update_namespace_picker (comm_nb_page->namespace_combo, ns, DIAG_COMM_ALL);
2876  }
2877 }
2878 
2879 
2880 /********************************************************************
2881  * gnc_ui_qif_import_commodity_all_notebook_pages_complete
2882  *
2883  * Scans all commodity notebook pages for the page_complete flag and
2884  * return TRUE if they are all complete
2885  ********************************************************************/
2886 static gboolean
2887 gnc_ui_qif_import_commodity_all_notebook_pages_complete (QIFImportWindow * wind)
2888 {
2889  GList *pageptr;
2890  GtkWidget *notebook_page;
2891  QIFCommNotebookPage *comm_nb_page;
2892  gboolean pages_complete = TRUE;
2893 
2894  for (pageptr = wind->commodity_notebook_pages; pageptr; pageptr = pageptr->next)
2895  {
2896  notebook_page = pageptr->data;
2897  comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
2898 
2899  if (!comm_nb_page->page_complete)
2900  pages_complete = FALSE;
2901  }
2902  return pages_complete;
2903 }
2904 
2905 
2906 /********************************************************************
2907 + * gnc_ui_qif_import_commodity_prepare
2908  ********************************************************************/
2909 void
2910 gnc_ui_qif_import_commodity_prepare (GtkAssistant *assistant, gpointer user_data)
2911 {
2912  QIFImportWindow *wind = user_data;
2913 
2914  /* Enable the Assistant "Next" Button */
2915  mark_page_complete (assistant,
2916  gnc_ui_qif_import_commodity_all_notebook_pages_complete (wind));
2917 
2918  /* If there are new securities, prepare the security pages. */
2919  if (wind->new_securities != SCM_BOOL_F)
2920  {
2921  wind->timeout_id = 0;
2922 
2923  /* add the commodity notebook pages */
2924  prepare_security_pages (wind);
2925 
2926  /* make sure all the namespace combos are in sync */
2927  gnc_ui_qif_import_commodity_notebook_update_combos (wind, TRUE);
2928  }
2929 }
2930 
2931 /********************************************************************
2932  * gnc_ui_qif_import_skip_commodity
2933  *
2934  * Determine if we need the import commodity page
2935  ********************************************************************/
2936 static gboolean
2937 gnc_ui_qif_import_skip_commodity (QIFImportWindow *wind)
2938 {
2939  return !gnc_ui_qif_import_new_securities (wind);
2940 }
2941 
2942 
2943 
2944 /*********************************
2945  * gnc_ui_qif_import_comm_valid
2946  ********************************/
2947 static gboolean
2948 gnc_ui_qif_import_comm_valid (GtkAssistant *assistant, gpointer user_data)
2949 {
2950  QIFImportWindow * wind = user_data;
2951  gint num = gtk_notebook_get_current_page (GTK_NOTEBOOK(wind->commodity_notebook));
2952  GtkWidget * notebook_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK(wind->commodity_notebook), num);
2953  QIFCommNotebookPage * comm_nb_page = g_object_get_data (G_OBJECT(notebook_page), "page_struct");
2954 
2955  QofBook *book;
2956  gnc_commodity_table *table;
2957  gnc_commodity_namespace *newns;
2958 
2959  gchar *name_space = gnc_ui_namespace_picker_ns (comm_nb_page->namespace_combo);
2960  const gchar *name = gtk_entry_get_text (GTK_ENTRY(comm_nb_page->name_entry));
2961  const gchar *mnemonic = gtk_entry_get_text (GTK_ENTRY(comm_nb_page->mnemonic_entry));
2962 
2963  /* set the page complete flag to TRUE to start with */
2964  comm_nb_page->page_complete = TRUE;
2965 
2966  if (!name || (name[0] == 0))
2967  {
2968  comm_nb_page->page_complete = FALSE;
2969  g_free (name_space);
2970  return FALSE;
2971  }
2972  else if (!mnemonic || (mnemonic[0] == 0))
2973  {
2974  comm_nb_page->page_complete = FALSE;
2975  g_free (name_space);
2976  return FALSE;
2977  }
2978  else if (!name_space || (name_space[0] == 0))
2979  {
2980  comm_nb_page->page_complete = FALSE;
2981  if (name_space)
2982  g_free (name_space);
2983  return FALSE;
2984  }
2985 
2986  /* FIXME: Should check whether a commodity with this namespace and
2987  * mnemonic already exists. If so, ask the user whether to use
2988  * the existing one, or go back and change what they've entered.
2989  */
2990 
2991  book = gnc_get_current_book ();
2993  if (gnc_commodity_namespace_is_iso (name_space) &&
2994  !gnc_commodity_table_lookup (table, name_space, mnemonic))
2995  {
2996  gnc_warning_dialog (GTK_WINDOW(assistant), "%s",
2997  _("You must enter an existing national "
2998  "currency or enter a different type."));
2999 
3000  comm_nb_page->page_complete = FALSE;
3001  g_free (name_space);
3002  return FALSE;
3003  }
3004 
3005  /* Is the namespace a new one? */
3006  if (!gnc_commodity_table_has_namespace (table, name_space))
3007  {
3008  /* Register it so that it will appear as an option on other pages. */
3009  newns = gnc_commodity_table_add_namespace (table, name_space, book);
3010 
3011  /* Remember it so it can be removed if the import gets canceled. */
3012  if (newns)
3013  wind->new_namespaces = g_list_prepend (wind->new_namespaces, name_space);
3014  else
3015  {
3016  g_warning ("QIF import: Couldn't create namespace %s", name_space);
3017  g_free (name_space);
3018  }
3019  }
3020  else
3021  g_free (name_space);
3022 
3023  /* make sure all the namespace combos are in sync */
3024  gnc_ui_qif_import_commodity_notebook_update_combos (wind, FALSE);
3025 
3026  return gnc_ui_qif_import_commodity_all_notebook_pages_complete (wind);
3027 }
3028 
3029 
3030 /*************************************
3031  * gnc_ui_qif_import_comm_changed_cb
3032  ************************************/
3033 void
3034 gnc_ui_qif_import_comm_changed_cb (GtkWidget *widget, gpointer user_data)
3035 {
3036  QIFImportWindow *wind = user_data;
3037  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
3038 
3039  mark_page_complete (assistant,
3040  gnc_ui_qif_import_comm_valid (assistant, user_data));
3041 }
3042 
3043 
3044 static gboolean
3045 do_page_check (gpointer user_data)
3046 {
3047  QIFImportWindow *wind = user_data;
3048  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
3049 
3050  mark_page_complete (assistant,
3051  gnc_ui_qif_import_comm_valid (assistant, wind));
3052 
3053  wind->timeout_id = 0;
3054  return FALSE;
3055 }
3056 
3057 
3058 /**********************************************
3059  * gnc_ui_qif_import_comm_namespace_changed_cb
3060  **********************************************/
3061 void
3062 gnc_ui_qif_import_comm_namespace_changed_cb (GtkWidget *widget, gpointer user_data)
3063 {
3064  QIFImportWindow *wind = user_data;
3065 
3066  if (wind->timeout_id)
3067  g_source_remove (wind->timeout_id);
3068 
3069  /* delay the page check while typing in the combo entry, this should
3070  * reduce the number of false entries as new name spaces are typed, its
3071  * not really a problem as name spaces created but not used do not get imported. */
3072  wind->timeout_id = g_timeout_add (500, (GSourceFunc)do_page_check, wind);
3073 }
3074 
3075 
3076 /**********************************************
3077  * Page 14 - Convert progress Page Procedures
3078  *********************************************/
3079 
3080 /********************************************************************
3081  * gnc_ui_qif_import_convert_progress_pause_cb
3082  *
3083  * Invoked when the "Pause" button is clicked.
3084  ********************************************************************/
3085 void
3086 gnc_ui_qif_import_convert_progress_pause_cb (GtkButton * button,
3087  gpointer user_data)
3088 {
3089  QIFImportWindow *wind = user_data;
3090  SCM toggle_pause = scm_c_eval_string ("qif-import:toggle-pause");
3091  SCM progress;
3092 
3093  if (!wind->busy)
3094  return;
3095 
3096  /* Create SCM for the progress helper. */
3097  progress = SWIG_NewPointerObj (wind->convert_progress,
3098  SWIG_TypeQuery ("_p__GNCProgressDialog"),
3099  0);
3100 
3101  /* Pause (or resume) the currently running operation. */
3102  scm_call_1 (toggle_pause, progress);
3103 
3104  /* Swap the button label between pause and resume. */
3105  if (strcmp (gtk_button_get_label (button), _("_Resume")))
3106  {
3107  gtk_button_set_use_underline (button, TRUE);
3108  gtk_button_set_label (button, _("_Resume"));
3109  }
3110  else
3111  {
3112  gtk_button_set_use_underline (button, FALSE);
3113  gtk_button_set_label (button, _("P_ause"));
3114  }
3115 }
3116 
3117 
3118 /********************************************************************
3119  * gnc_ui_qif_import_convert_progress_start_cb
3120  *
3121  * Invoked when the "Start" button is clicked.
3122  ********************************************************************/
3123 void
3124 gnc_ui_qif_import_convert_progress_start_cb (GtkButton * button,
3125  gpointer user_data)
3126 {
3127  QIFImportWindow *wind = user_data;
3128  GtkAssistant *assistant = GTK_ASSISTANT(wind->window);
3129 
3130  SCM qif_to_gnc = scm_c_eval_string ("qif-import:qif-to-gnc");
3131  SCM find_duplicates = scm_c_eval_string ("gnc:account-tree-find-duplicates");
3132  SCM retval;
3133 
3134  /* SCM for the progress dialog. */
3135  SCM progress = SWIG_NewPointerObj (wind->convert_progress,
3136  SWIG_TypeQuery ("_p__GNCProgressDialog"),
3137  0);
3138 
3139  /* The default currency. */
3140  const gchar *currname = gtk_entry_get_text (GTK_ENTRY(gtk_bin_get_child (
3141  GTK_BIN(GTK_COMBO_BOX(wind->currency_picker)))));
3142 
3143  /* Raise the busy flag so the assistant can't be canceled unexpectedly. */
3144  wind->busy = TRUE;
3145  gtk_widget_set_sensitive (wind->convert_pause, TRUE);
3146  gtk_widget_set_sensitive (wind->convert_start, FALSE);
3147 
3148  /* Clear any previous pause or cancel state. */
3149  scm_c_eval_string ("(qif-import:reset-cancel-pause)");
3150 
3151  /* Update the commodities. */
3152  gnc_ui_qif_import_commodity_update (wind);
3153 
3154  /*
3155  * Convert the QIF data into GnuCash data.
3156  *
3157  * A Scheme function does all the work. The return value is the
3158  * root account of an account tree containing all the new accounts
3159  * and transactions. Upon failure, #f is returned. If the user
3160  * cancels, #t is returned.
3161  */
3162 
3163  /* This step will fill 70% of the bar. */
3164  gnc_progress_dialog_push (wind->convert_progress, 0.7);
3165  retval = scm_apply (qif_to_gnc,
3166  scm_list_n (wind->imported_files,
3167  wind->acct_map_info,
3168  wind->cat_map_info,
3169  wind->memo_map_info,
3170  wind->security_hash,
3171  scm_from_utf8_string (currname ? currname : ""),
3172  wind->transaction_status,
3173  progress,
3174  SCM_UNDEFINED),
3175  SCM_EOL);
3176  gnc_progress_dialog_pop (wind->convert_progress);
3177 
3178  if (retval == SCM_BOOL_T)
3179  {
3180  /* Canceled by the user. */
3181 
3182  /* Disable the pause button. */
3183  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3184 
3185  /* Remove any converted data. */
3186  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3187  gnc_ui_qif_import_convert_undo (wind);
3188 
3189  /* Inform the user. */
3190  gnc_progress_dialog_set_sub (wind->convert_progress, _("Canceled"));
3191  gnc_progress_dialog_reset_value (wind->convert_progress);
3192 
3193  wind->busy = FALSE;
3194  wind->load_stop = TRUE;
3195  }
3196  else if (retval == SCM_BOOL_F)
3197  {
3198  /* An bug was encountered during conversion. */
3199 
3200  /* Disable the pause button. */
3201  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3202 
3203  /* Remove any converted data. */
3204  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3205  gnc_ui_qif_import_convert_undo (wind);
3206 
3207  /* Inform the user. */
3208  gnc_progress_dialog_append_log (wind->convert_progress,
3209  _("A bug was detected while converting the QIF data."));
3210  gnc_progress_dialog_set_sub (wind->convert_progress, _("Failed"));
3211  gnc_progress_dialog_reset_value (wind->convert_progress);
3212  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
3213  _("A bug was detected while converting the QIF data."));
3214  /* FIXME: How should we request that the user report this problem? */
3215 
3216  wind->busy = FALSE;
3217  wind->load_stop = TRUE;
3218  }
3219  else if (scm_is_symbol (retval))
3220  {
3221  /* An error was encountered during conversion. */
3222 
3223  /* Disable the pause button. */
3224  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3225 
3226  /* Remove any converted data. */
3227  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3228  gnc_ui_qif_import_convert_undo (wind);
3229 
3230  /* Inform the user. */
3231  gnc_progress_dialog_set_sub (wind->convert_progress, _("Failed"));
3232  gnc_progress_dialog_reset_value (wind->convert_progress);
3233 
3234  wind->busy = FALSE;
3235  wind->load_stop = TRUE;
3236  }
3237  if (wind->load_stop == FALSE)
3238  {
3239  /* Save the imported account tree. */
3240  scm_gc_unprotect_object (wind->imported_account_tree);
3241  wind->imported_account_tree = retval;
3242  scm_gc_protect_object (wind->imported_account_tree);
3243 
3244  /*
3245  * Detect potentially duplicated transactions.
3246  */
3247 
3248  /* This step will fill the remainder of the bar. */
3249  gnc_progress_dialog_push (wind->convert_progress, 1);
3250  retval = scm_call_3 (find_duplicates,
3251  scm_c_eval_string ("(gnc-get-current-root-account)"),
3252  wind->imported_account_tree, progress);
3253  gnc_progress_dialog_pop (wind->convert_progress);
3254 
3255  /* Save the results. */
3256  scm_gc_unprotect_object (wind->match_transactions);
3257  wind->match_transactions = retval;
3258  scm_gc_protect_object (wind->match_transactions);
3259 
3260  if (retval == SCM_BOOL_T)
3261  {
3262  /* Canceled by the user. */
3263  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3264  gnc_progress_dialog_set_sub (wind->convert_progress, _("Canceling"));
3265  wind->busy = FALSE;
3266  wind->load_stop = TRUE;
3267  }
3268  else if (retval == SCM_BOOL_F)
3269  {
3270  /* An error occurred during duplicate checking. */
3271 
3272  /* Remove any converted data. */
3273  gnc_progress_dialog_set_sub (wind->convert_progress, _("Cleaning up"));
3274  gnc_ui_qif_import_convert_undo (wind);
3275 
3276  /* Inform the user. */
3277  gnc_progress_dialog_append_log (wind->convert_progress,
3278  _("A bug was detected while detecting duplicates."));
3279  gnc_progress_dialog_set_sub (wind->convert_progress, _("Failed"));
3280  gnc_progress_dialog_reset_value (wind->convert_progress);
3281  gnc_error_dialog (GTK_WINDOW(assistant), "%s",
3282  _("A bug was detected while detecting duplicates."));
3283  /* FIXME: How should we request that the user report this problem? */
3284 
3285  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3286  wind->busy = FALSE;
3287  wind->load_stop = TRUE;
3288  }
3289  }
3290  /* Enable the Assistant "Next" Button */
3291  mark_page_complete (assistant, TRUE);
3292 
3293  /* Set Pause and Start buttons */
3294  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3295  gtk_widget_set_sensitive (wind->convert_start, FALSE);
3296 
3297  if (wind->load_stop == FALSE)
3298  {
3299  /* The conversion completed successfully. */
3300  gnc_progress_dialog_set_sub (wind->convert_progress,
3301  _("Conversion completed"));
3302  gnc_progress_dialog_set_value (wind->convert_progress, 1);
3303 
3304  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3305  wind->busy = FALSE;
3306 
3307  /* If the log is empty, move on to the next page automatically. */
3308  if (gtk_text_buffer_get_char_count (gtk_text_view_get_buffer (GTK_TEXT_VIEW(wind->convert_log))) == 0) {
3309  gtk_assistant_next_page (assistant);
3310  }
3311  }
3312 }
3313 
3314 
3315 /********************************************************************
3316  * gnc_ui_qif_import_convert_progress_prepare
3317  *
3318  * Prepare the data conversion progress page for display.
3319  ********************************************************************/
3320 void
3321 gnc_ui_qif_import_convert_progress_prepare (GtkAssistant *assistant,
3322  gpointer user_data)
3323 {
3324  QIFImportWindow *wind = user_data;
3325 
3326  /* Reset the progress display. */
3327  gnc_progress_dialog_set_primary (wind->convert_progress, "");
3328  gnc_progress_dialog_set_secondary (wind->convert_progress,
3329  _("When you press the Start Button, GnuCash will import your QIF data. "
3330  "If there are no errors or warnings, you will automatically proceed to "
3331  "the next step. Otherwise, the details will be shown below for your review."));
3332  gnc_progress_dialog_set_sub (wind->convert_progress, " ");
3333  gnc_progress_dialog_reset_value (wind->convert_progress);
3334  gnc_progress_dialog_reset_log (wind->convert_progress);
3335 
3336  /* Set Pause and Start buttons */
3337  gtk_widget_set_sensitive (wind->convert_pause, FALSE);
3338  gtk_widget_set_sensitive (wind->convert_start, TRUE);
3339 
3340  /* Disable the assistant "Next" button */
3341  mark_page_complete (assistant, FALSE);
3342 
3343  /* Before creating transactions, if this is a new book, let user specify
3344  * book options, since they affect how transactions are created */
3345  if (wind->new_book)
3346  wind->new_book = gnc_new_book_option_display (wind->window);
3347 }
3348 
3349 
3350 /*****************************************
3351  * Page 15 - Match Doc. Page Procedures
3352  *****************************************/
3353 
3354 /********************************************************************
3355  * gnc_ui_qif_import_duplicates_doc_prepare
3356  ********************************************************************/
3357 void
3358 gnc_ui_qif_import_duplicates_doc_prepare (GtkAssistant *assistant,
3359  gpointer user_data)
3360 {
3361  /* Enable the Assistant "Next" Button */
3362  mark_page_complete (assistant, TRUE);
3363 
3364 }
3365 
3366 /********************************************************************
3367  * gnc_ui_qif_import_skip_duplicates_doc
3368  *
3369  * Determine if we need the import duplicates doc page
3370  ********************************************************************/
3371 static gboolean
3372 gnc_ui_qif_import_skip_duplicates_doc (QIFImportWindow *wind)
3373 {
3374  /* Jump over doc page if show_doc_pages FALSE */
3375  if (!wind->show_doc_pages)
3376  return TRUE;
3377 
3378  /* Don't show doc page if there are no duplicates */
3379  if (scm_is_null (wind->match_transactions))
3380  return TRUE;
3381 
3382  return FALSE;
3383 }
3384 
3385 /**********************************************
3386  * Page 16 - Match Duplicates Page Procedures
3387  **********************************************/
3388 
3389 /********************************************************************
3390  * gnc_ui_qif_import_duplicates_match_prepare
3391  ********************************************************************/
3392 void
3393 gnc_ui_qif_import_duplicates_match_prepare (GtkAssistant *assistant,
3394  gpointer user_data)
3395 {
3396  QIFImportWindow * wind = user_data;
3397 
3398  GtkTreeView *view;
3399  GtkListStore *store;
3400  SCM duplicates;
3401  SCM current_xtn;
3402  Transaction *gnc_xtn;
3403  Split *gnc_split;
3404  GtkTreeIter iter;
3405  GtkTreeSelection *selection;
3406  GtkTreePath *path;
3407  const gchar *amount_str;
3408  int rownum = 0;
3409 
3410  if (!scm_is_null (wind->match_transactions))
3411  {
3412  view = GTK_TREE_VIEW(wind->new_transaction_view);
3413  store = GTK_LIST_STORE(gtk_tree_view_get_model (view));
3414  gtk_list_store_clear (store);
3415 
3416  if (!scm_is_list (wind->match_transactions))
3417  return;
3418 
3419  /* Loop through the list of new, potentially duplicate transactions. */
3420  duplicates = wind->match_transactions;
3421  while (!scm_is_null (duplicates))
3422  {
3423  gdouble amount_gd = 0;
3424  time64 send_time = 0;
3425  char datebuff [MAX_DATE_LENGTH + 1];
3426  memset (datebuff, 0, MAX_DATE_LENGTH);
3427  current_xtn = SCM_CAAR(duplicates);
3428 #define FUNC_NAME "xaccTransCountSplits"
3429  gnc_xtn = SWIG_MustGetPtr (current_xtn,
3430  SWIG_TypeQuery ("_p_Transaction"), 1, 0);
3431 #undef FUNC_NAME
3432  if (xaccTransCountSplits (gnc_xtn) > 2)
3433  amount_str = _("(split)");
3434  else
3435  {
3436  gnc_split = xaccTransGetSplit (gnc_xtn, 0);
3437  amount_str =
3439  gnc_account_print_info
3440  (xaccSplitGetAccount (gnc_split), TRUE));
3441  amount_gd = gnc_numeric_to_double (xaccSplitGetValue(gnc_split));
3442  }
3443  gtk_list_store_append (store, &iter);
3444  send_time = xaccTransRetDatePosted (gnc_xtn);
3445  qof_print_date_buff (datebuff, MAX_DATE_LENGTH, send_time);
3446  gtk_list_store_set
3447  (store, &iter,
3448  QIF_TRANS_COL_INDEX, rownum++,
3449  QIF_TRANS_COL_DATE, datebuff,
3450  QIF_TRANS_COL_DATE_INT64, send_time, // used for sorting
3451  QIF_TRANS_COL_DESCRIPTION, xaccTransGetDescription (gnc_xtn),
3452  QIF_TRANS_COL_AMOUNT, amount_str,
3453  QIF_TRANS_COL_AMOUNT_DOUBLE, amount_gd, // used for sorting
3454  -1);
3455 
3456  duplicates = SCM_CDR(duplicates);
3457  }
3458  selection = gtk_tree_view_get_selection (view);
3459  path = gtk_tree_path_new_from_indices (0, -1);
3460  gtk_tree_selection_select_path (selection, path);
3461  gtk_tree_path_free (path);
3462  }
3463 
3464  /* Enable the Assistant "Next" Button */
3465  mark_page_complete (assistant, TRUE);
3466 }
3467 
3468 /********************************************************************
3469  * gnc_ui_qif_import_skip_duplicates_match
3470  *
3471  * Determine if we need the import duplicates match page
3472  ********************************************************************/
3473 static gboolean
3474 gnc_ui_qif_import_skip_duplicates_match (QIFImportWindow *wind)
3475 {
3476  /* Don't show page if there are no duplicates */
3477  return scm_is_null (wind->match_transactions);
3478 }
3479 
3480 
3481 /*************************************
3482  * Page 17 - Apply Page Procedures
3483  *************************************/
3484 
3485 /********************************************************************
3486  * gnc_ui_qif_import_end_page_prepare
3487  ********************************************************************/
3488 void
3489 gnc_ui_qif_import_end_page_prepare (GtkAssistant *assistant,
3490  gpointer user_data)
3491 {
3492  /* Enable the Assistant "Next" Button */
3493  mark_page_complete (assistant, TRUE);
3494 }
3495 
3496 
3497 /********************************************************************
3498  * gnc_ui_qif_import_finish_cb
3499  *
3500  * Invoked when the "Apply" button is clicked on the final page.
3501  ********************************************************************/
3502 void
3503 gnc_ui_qif_import_finish_cb (GtkAssistant *assistant,
3504  gpointer user_data)
3505 {
3506  QIFImportWindow * wind = user_data;
3507 
3508  SCM save_map_prefs = scm_c_eval_string ("qif-import:save-map-prefs");
3509  SCM cat_and_merge = scm_c_eval_string ("gnc:account-tree-catenate-and-merge");
3510  SCM prune_xtns = scm_c_eval_string ("gnc:prune-matching-transactions");
3511  SCM scm_result;
3512 
3513  GncPluginPage *page;
3514  gboolean acct_tree_found = FALSE;
3515 
3516  gnc_suspend_gui_refresh ();
3517 
3518  /* Prune any imported transactions that were determined to be duplicates. */
3519  if (wind->match_transactions != SCM_BOOL_F)
3520  scm_call_1 (prune_xtns, wind->match_transactions);
3521 
3522  /* Merge the imported account tree with the existing one. */
3523  if (wind->imported_account_tree != SCM_BOOL_F)
3524  scm_call_2 (cat_and_merge,
3525  scm_c_eval_string ("(gnc-get-current-root-account)"),
3526  wind->imported_account_tree);
3527 
3528  gnc_resume_gui_refresh ();
3529 
3530  /* Save the user's mapping preferences. */
3531  scm_result = scm_apply (save_map_prefs,
3532  scm_list_5 (wind->acct_map_info, wind->cat_map_info,
3533  wind->memo_map_info, wind->security_hash,
3534  wind->security_prefs),
3535  SCM_EOL);
3536 
3537  if (scm_result == SCM_BOOL_F)
3538  gnc_warning_dialog (GTK_WINDOW(assistant), "%s",
3539  _("GnuCash was unable to save your mapping preferences."));
3540 
3541  /* Open an account tab in the main window if one doesn't exist already. */
3542  gnc_main_window_foreach_page (gnc_ui_qif_import_check_acct_tree,
3543  &acct_tree_found);
3544 
3545  wind->acct_tree_found = acct_tree_found;
3546  if (!acct_tree_found)
3547  {
3549  gnc_main_window_open_page (NULL, page);
3550  }
3551 }
3552 
3553 
3554 /***************************************
3555  * Page 18 - Summary Page Procedures
3556  ***************************************/
3557 
3558 /********************************************************************
3559  * gnc_ui_qif_import_summary_page_prepare
3560  ********************************************************************/
3561 void
3562 gnc_ui_qif_import_summary_page_prepare (GtkAssistant *assistant,
3563  gpointer user_data)
3564 {
3565  QIFImportWindow * wind = user_data;
3566 
3567  const gchar *msg = wind->load_stop ?
3568  _("There was a problem with the import.") :
3569  _("QIF Import Completed.");
3570 
3571  gchar *text = g_markup_printf_escaped ("<span size=\"large\"><b>%s</b></span>", msg);
3572 
3573  gtk_label_set_markup (GTK_LABEL(wind->summary_text), text);
3574 
3575  g_free (text);
3576 
3577  /* Enable the Assistant "Next" Button */
3578  mark_page_complete (assistant, TRUE);
3579 }
3580 
3581 
3582 /********************************************************************
3583  * Prepare callback for assistant pages.
3584  ********************************************************************/
3585 void gnc_ui_qif_import_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
3586  gpointer user_data)
3587 {
3588  gint currentpage = gtk_assistant_get_current_page (assistant);
3589  GtkWidget *mypage = gtk_assistant_get_nth_page (assistant, currentpage);
3590  const char *pagename = gtk_buildable_get_name (GTK_BUILDABLE(mypage));
3591 
3592  ENTER("Page %s", pagename);
3593 
3594  if (!g_strcmp0 (pagename, "start_page"))
3595  {
3596  /* Current page is Intro page */
3597  gnc_ui_qif_import_intro_prepare (assistant, user_data);
3598  }
3599  else if (!g_strcmp0 (pagename, "load_file_page"))
3600  {
3601  /* Current page is File Load */
3602  gnc_ui_qif_import_load_file_prepare (assistant, user_data);
3603  }
3604  else if (!g_strcmp0 (pagename, "load_progress_page"))
3605  {
3606  /* Current page is Load Progress */
3607  gnc_ui_qif_import_load_progress_prepare (assistant, user_data);
3608  }
3609  else if (!g_strcmp0 (pagename, "date_format_page"))
3610  {
3611  /* Current page is date page */
3612  /* No preparation required */
3613  }
3614  else if (!g_strcmp0 (pagename, "account_name_page"))
3615  {
3616  /* Current page is account page */
3617  gnc_ui_qif_import_account_prepare (assistant, user_data);
3618  }
3619  else if (!g_strcmp0 (pagename, "loaded_files_page"))
3620  {
3621  /* Current page is loaded files page */
3622  gnc_ui_qif_import_loaded_files_prepare (assistant, user_data);
3623  }
3624  else if (!g_strcmp0 (pagename, "account_doc_page"))
3625  {
3626  /* Current page is Account Doc. page */
3627  gnc_ui_qif_import_account_doc_prepare (assistant, user_data);
3628  }
3629  else if (!g_strcmp0 (pagename, "account_match_page"))
3630  {
3631  /* Current page is Account Match page */
3632  gnc_ui_qif_import_account_match_prepare (assistant, user_data);
3633  }
3634  else if (!g_strcmp0 (pagename, "category_doc_page"))
3635  {
3636  /* Current page is Category Doc. page */
3637  gnc_ui_qif_import_category_doc_prepare (assistant, user_data);
3638  }
3639  else if (!g_strcmp0 (pagename, "category_match_page"))
3640  {
3641  /* Current page is Category Match page */
3642  gnc_ui_qif_import_category_match_prepare (assistant, user_data);
3643  }
3644  else if (!g_strcmp0 (pagename, "memo_doc_page"))
3645  {
3646  /* Current page is Memo Doc. page */
3647  gnc_ui_qif_import_memo_doc_prepare (assistant, user_data);
3648  }
3649  else if (!g_strcmp0 (pagename, "memo_match_page"))
3650  {
3651  /* Current page is Memo Match page */
3652  gnc_ui_qif_import_memo_match_prepare (assistant, user_data);
3653  }
3654  else if (!g_strcmp0 (pagename, "currency_book_option_page"))
3655  {
3656  /* Current page is Currency page */
3657  gnc_ui_qif_import_currency_prepare (assistant, user_data);
3658  }
3659  else if (!g_strcmp0 (pagename, "commodity_page"))
3660  {
3661  /* Current page is Commodity page */
3662  gnc_ui_qif_import_commodity_prepare (assistant, user_data);
3663  }
3664  else if (!g_strcmp0 (pagename, "convert_progress_page"))
3665  {
3666  /* Current page is Conversion progress page */
3667  gnc_ui_qif_import_convert_progress_prepare (assistant, user_data);
3668  }
3669  else if (!g_strcmp0 (pagename, "duplicates_doc_page"))
3670  {
3671  /* Current page is Duplicates Doc page */
3672  gnc_ui_qif_import_duplicates_doc_prepare (assistant, user_data);
3673  }
3674  else if (!g_strcmp0 (pagename, "duplicates_match_page"))
3675  {
3676  /* Current page is Duplicates Match page */
3677  gnc_ui_qif_import_duplicates_match_prepare (assistant, user_data);
3678  }
3679  else if (!g_strcmp0 (pagename, "end_page"))
3680  {
3681  /* Current page is the end page */
3682  gnc_ui_qif_import_end_page_prepare (assistant, user_data);
3683  }
3684  else if (!g_strcmp0 (pagename, "summary_page"))
3685  {
3686  /* Current page is the summary page */
3687  gnc_ui_qif_import_summary_page_prepare (assistant, user_data);
3688  }
3689  LEAVE("");
3690 }
3691 
3692 
3693 /********************************************************************
3694  * get_assistant_widgets
3695  *
3696  * Get all builder-defined widgets that need to be actively managed.
3697  ********************************************************************/
3698 static void
3699 get_assistant_widgets (QIFImportWindow *wind, GtkBuilder *builder)
3700 {
3701  g_return_if_fail (wind);
3702  g_return_if_fail (builder);
3703 
3704  wind->window = GTK_WIDGET(gtk_builder_get_object (builder, "qif_import_assistant"));
3705  wind->filename_entry = GTK_WIDGET(gtk_builder_get_object (builder, "qif_filename_entry"));
3706  wind->load_pause = GTK_WIDGET(gtk_builder_get_object (builder, "load_progress_pause"));
3707  wind->load_start = GTK_WIDGET(gtk_builder_get_object (builder, "load_progress_start"));
3708  wind->load_log = GTK_WIDGET(gtk_builder_get_object (builder, "load_progress_log"));
3709  wind->load_progress = gnc_progress_dialog_custom (
3710  GTK_LABEL(gtk_builder_get_object (builder, "load_progress_primary")),
3711  GTK_LABEL(gtk_builder_get_object (builder, "load_progress_secondary")),
3712  GTK_PROGRESS_BAR(gtk_builder_get_object (builder, "load_progress_bar")),
3713  GTK_LABEL(gtk_builder_get_object (builder, "load_progress_sub")),
3714  GTK_TEXT_VIEW(wind->load_log));
3715  wind->acct_entry = GTK_WIDGET(gtk_builder_get_object (builder, "qif_account_entry"));
3716  wind->date_format_combo = GTK_WIDGET(gtk_builder_get_object (builder, "date_format_combobox"));
3717  wind->selected_file_view = GTK_WIDGET(gtk_builder_get_object (builder, "selected_file_view"));
3718  wind->unload_file_btn = GTK_WIDGET(gtk_builder_get_object (builder, "unload_file_button"));
3719  wind->currency_picker = GTK_WIDGET(gtk_builder_get_object (builder, "currency_comboboxentry"));
3720  wind->book_option_label = GTK_WIDGET(gtk_builder_get_object (builder, "book_option_label"));
3721  wind->book_option_message = GTK_WIDGET(gtk_builder_get_object (builder, "book_option_message_label"));
3722  wind->commodity_notebook = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_notebook"));
3723  wind->acct_view = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_view"));
3724  wind->acct_view_count = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_count"));
3725  wind->acct_view_btn = GTK_WIDGET(gtk_builder_get_object (builder, "account_page_change"));
3726  wind->cat_view = GTK_WIDGET(gtk_builder_get_object (builder, "category_page_view"));
3727  wind->cat_view_count = GTK_WIDGET(gtk_builder_get_object (builder, "category_page_count"));
3728  wind->cat_view_btn = GTK_WIDGET(gtk_builder_get_object (builder, "category_page_change"));
3729  wind->memo_view = GTK_WIDGET(gtk_builder_get_object (builder, "memo_page_view"));
3730  wind->memo_view_count = GTK_WIDGET(gtk_builder_get_object (builder, "memo_page_count"));
3731  wind->memo_view_btn = GTK_WIDGET(gtk_builder_get_object (builder, "memo_page_change"));
3732  wind->convert_pause = GTK_WIDGET(gtk_builder_get_object (builder, "convert_progress_pause"));
3733  wind->convert_start = GTK_WIDGET(gtk_builder_get_object (builder, "convert_progress_start"));
3734  wind->convert_log = GTK_WIDGET(gtk_builder_get_object (builder, "convert_progress_log"));
3735  wind->convert_progress = gnc_progress_dialog_custom (
3736  GTK_LABEL(gtk_builder_get_object (builder, "convert_progress_primary")),
3737  GTK_LABEL(gtk_builder_get_object (builder, "convert_progress_secondary")),
3738  GTK_PROGRESS_BAR(gtk_builder_get_object (builder, "convert_progress_bar")),
3739  GTK_LABEL(gtk_builder_get_object (builder, "convert_progress_sub")),
3740  GTK_TEXT_VIEW(wind->convert_log));
3741  wind->summary_text = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page"));
3742 
3743  // Set the name for this assistant so it can be easily manipulated with css
3744  gtk_widget_set_name (GTK_WIDGET(wind->window), "gnc-id-assistant-qif-import");
3745  gnc_widget_style_context_add_class (GTK_WIDGET(wind->window), "gnc-class-imports");
3746 
3747  wind->new_transaction_view =
3748  GTK_WIDGET(gtk_builder_get_object (builder, "new_transaction_view"));
3749  wind->old_transaction_view =
3750  GTK_WIDGET(gtk_builder_get_object (builder, "old_transaction_view"));
3751 }
3752 
3753 
3754 /********************************************************************
3755  * build_views
3756  *
3757  * Build the details of all GtkTreeView widgets.
3758  ********************************************************************/
3759 static void
3760 build_views (QIFImportWindow *wind)
3761 {
3762  GtkTreeView *view;
3763  GtkListStore *store;
3764  GtkCellRenderer *renderer;
3765  GtkTreeViewColumn *column;
3766  GtkTreeSelection *selection;
3767 
3768  g_return_if_fail (wind);
3769 
3770  /* Set up the selected file view */
3771  view = GTK_TREE_VIEW(wind->selected_file_view);
3772  store = gtk_list_store_new (NUM_FILENAME_COLS, G_TYPE_INT, G_TYPE_STRING);
3773  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
3774  g_object_unref (store);
3775 
3776  renderer = gtk_cell_renderer_text_new ();
3777  column = gtk_tree_view_column_new_with_attributes ("unused",
3778  renderer,
3779  "text",
3780  FILENAME_COL_NAME,
3781  NULL);
3782  gtk_tree_view_append_column (view, column);
3783 
3784  selection = gtk_tree_view_get_selection (view);
3785  g_signal_connect (selection, "changed",
3786  G_CALLBACK(gnc_ui_qif_import_select_loaded_file_cb),
3787  wind);
3788 
3789  /* Set up the QIF account to GnuCash account matcher. */
3790  create_account_picker_view (wind->acct_view, _("QIF account name"),
3791  G_CALLBACK(gnc_ui_qif_import_account_activate_cb),
3792  G_CALLBACK(gnc_ui_qif_import_account_select_cb),
3793  wind);
3794 
3795  /* Set up the QIF category to GnuCash account matcher. */
3796  create_account_picker_view (wind->cat_view, _("QIF category name"),
3797  G_CALLBACK(gnc_ui_qif_import_category_activate_cb),
3798  G_CALLBACK(gnc_ui_qif_import_category_select_cb),
3799  wind);
3800 
3801  /* Set up the QIF payee/memo to GnuCash account matcher. */
3802  create_account_picker_view (wind->memo_view, _("QIF payee/memo"),
3803  G_CALLBACK(gnc_ui_qif_import_memo_activate_cb),
3804  G_CALLBACK(gnc_ui_qif_import_memo_select_cb),
3805  wind);
3806 
3807  /* Set up the new transaction view */
3808  view = GTK_TREE_VIEW(wind->new_transaction_view);
3809  store = gtk_list_store_new (NUM_QIF_TRANS_COLS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT64,
3810  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_BOOLEAN);
3811  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
3812 
3813  /* default sort order */
3814  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
3815  QIF_TRANS_COL_DATE_INT64,
3816  GTK_SORT_ASCENDING);
3817  g_object_unref (store);
3818 
3819  /* prevent the rows being dragged to a different order */
3820  gtk_tree_view_set_reorderable (view, FALSE);
3821 
3822  renderer = gtk_cell_renderer_text_new ();
3823  column = gtk_tree_view_column_new_with_attributes (_("Date"),
3824  renderer,
3825  "text",
3826  QIF_TRANS_COL_DATE,
3827  NULL);
3828  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3829  gtk_tree_view_append_column (view, column);
3830  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DATE_INT64);
3831 
3832  renderer = gtk_cell_renderer_text_new ();
3833  column = gtk_tree_view_column_new_with_attributes (_("Description"),
3834  renderer,
3835  "text",
3836  QIF_TRANS_COL_DESCRIPTION,
3837  NULL);
3838  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3839  gtk_tree_view_append_column (view, column);
3840  gtk_tree_view_column_set_expand(column, TRUE);
3841  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DESCRIPTION);
3842 
3843  renderer = gtk_cell_renderer_text_new ();
3844  column = gtk_tree_view_column_new_with_attributes (_("Amount"),
3845  renderer,
3846  "text",
3847  QIF_TRANS_COL_AMOUNT,
3848  NULL);
3849  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3850  gtk_tree_view_append_column (view, column);
3851  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_AMOUNT_DOUBLE);
3852 
3853  selection = gtk_tree_view_get_selection (view);
3854  g_signal_connect (selection, "changed",
3855  G_CALLBACK(gnc_ui_qif_import_duplicate_new_select_cb),
3856  wind);
3857 
3858  /* Set up the old transaction view */
3859  view = GTK_TREE_VIEW(wind->old_transaction_view);
3860  store = gtk_list_store_new (NUM_QIF_TRANS_COLS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT64,
3861  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_BOOLEAN);
3862  gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
3863 
3864  /* default sort order */
3865  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
3866  QIF_TRANS_COL_DATE_INT64,
3867  GTK_SORT_ASCENDING);
3868  g_object_unref (store);
3869 
3870  /* prevent the rows being dragged to a different order */
3871  gtk_tree_view_set_reorderable (view, FALSE);
3872 
3873  renderer = gtk_cell_renderer_text_new ();
3874  column = gtk_tree_view_column_new_with_attributes (_("Date"),
3875  renderer,
3876  "text",
3877  QIF_TRANS_COL_DATE,
3878  NULL);
3879  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3880  gtk_tree_view_append_column (view, column);
3881  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DATE_INT64);
3882 
3883  renderer = gtk_cell_renderer_text_new ();
3884  column = gtk_tree_view_column_new_with_attributes (_("Description"),
3885  renderer,
3886  "text",
3887  QIF_TRANS_COL_DESCRIPTION,
3888  NULL);
3889  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3890  gtk_tree_view_append_column (view, column);
3891  gtk_tree_view_column_set_expand (column, TRUE);
3892  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_DESCRIPTION);
3893 
3894  renderer = gtk_cell_renderer_text_new ();
3895  column = gtk_tree_view_column_new_with_attributes (_("Amount"),
3896  renderer,
3897  "text",
3898  QIF_TRANS_COL_AMOUNT,
3899  NULL);
3900  g_object_set (G_OBJECT(column), "reorderable", TRUE, "resizable", TRUE, NULL);
3901  gtk_tree_view_append_column (view, column);
3902  gtk_tree_view_column_set_sort_column_id (column, QIF_TRANS_COL_AMOUNT_DOUBLE);
3903 
3904  renderer = gtk_cell_renderer_toggle_new ();
3905  column = gtk_tree_view_column_new_with_attributes (_("Match?"),
3906  renderer,
3907  "active",
3908  QIF_TRANS_COL_CHECKED,
3909  NULL);
3910  gtk_tree_view_append_column (view, column);
3911 
3912  selection = gtk_tree_view_get_selection (view);
3913  g_signal_connect (selection, "changed",
3914  G_CALLBACK(gnc_ui_qif_import_duplicate_old_select_cb),
3915  wind);
3916 }
3917 
3918 
3919 /********************************************************************
3920  * gnc_ui_qif_import_assistant_make
3921  *
3922  * Build a new QIF import assistant.
3923  ********************************************************************/
3924 static GtkWidget *
3925 gnc_ui_qif_import_assistant_make (QIFImportWindow *qif_win)
3926 {
3927  GtkBuilder *builder;
3928  GtkWidget *box;
3929 
3930  builder = gtk_builder_new ();
3931  gnc_builder_add_from_file (builder, "assistant-qif-import.glade", "date_format_liststore");
3932  gnc_builder_add_from_file (builder, "assistant-qif-import.glade", "qif_import_assistant");
3933 
3934  qif_win->new_namespaces = NULL;
3935  qif_win->selected_transaction = 0;
3936  qif_win->busy = FALSE;
3937  /* In order to include a book options display on the creation of a new book,
3938  * we need to detect when we are dealing with a new book. */
3939  qif_win->new_book = gnc_is_new_book ();
3940 
3941  /* Get all user preferences related to QIF importing. */
3942  get_preferences (qif_win);
3943 
3944  /* Set up the Scheme side of things. Note that if a session/book did not
3945  * exist prior to this function, it is created within scheme function
3946  * "qif-import:load-map-prefs", so we need to have set the flag previously */
3947  initialize_scheme (qif_win);
3948 
3949  /* Get all interesting builder-defined widgets. */
3950  get_assistant_widgets (qif_win, builder);
3951  GtkAssistant *assistant = GTK_ASSISTANT(qif_win->window);
3952 
3953  /* Make this window stay on top */
3954  gtk_window_set_transient_for (GTK_WINDOW(qif_win->window), gnc_ui_get_main_window (NULL));
3955 
3956  /* Build the details of all GtkTreeView widgets. */
3957  build_views (qif_win);
3958  PINFO ("Total Number of Assistant Pages is %d", gtk_assistant_get_n_pages (assistant));
3959 
3960  /* Establish a custom next page function. */
3961  gtk_assistant_set_forward_page_func(assistant,
3962  gnc_ui_qif_import_assistant_page_forward, qif_win, NULL);
3963 
3964  /* Currency Page */
3965  /* Set a default currency for new accounts */
3966  qif_win->currency_picker = gnc_currency_edit_new ();
3967  gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(qif_win->currency_picker), gnc_default_currency ());
3968  gtk_widget_show (qif_win->currency_picker);
3969  box = GTK_WIDGET(gtk_builder_get_object (builder, "currency_picker_hbox"));
3970  gtk_box_pack_start (GTK_BOX(box), qif_win->currency_picker, TRUE, TRUE, 0);
3971 
3972  gnc_restore_window_size (GNC_PREFS_GROUP,
3973  GTK_WINDOW(qif_win->window), gnc_ui_get_main_window (NULL));
3974 
3975  g_signal_connect (qif_win->window, "destroy",
3976  G_CALLBACK(gnc_ui_qif_import_assistant_destroy), qif_win);
3977 
3978  gtk_builder_connect_signals (builder, qif_win);
3979 
3980  g_object_unref (G_OBJECT(builder));
3981 
3982  gtk_widget_show_all (qif_win->window);
3983  gtk_window_present (GTK_WINDOW(qif_win->window));
3984 
3985  return qif_win->window;
3986 }
3987 
3988 
3989 /********************************************
3990  * gnc_ui_qif_import_assistant_close_handler
3991  ********************************************/
3992 static void
3993 gnc_ui_qif_import_assistant_close_handler (gpointer user_data)
3994 {
3995  QIFImportWindow *qif_win = user_data;
3996 
3997  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(qif_win->window));
3998  gtk_widget_destroy (qif_win->window);
3999 }
4000 
4001 
4002 /********************************************
4003  * gnc_file_qif_import
4004  ********************************************/
4005 void
4006 gnc_file_qif_import (void)
4007 {
4008  QIFImportWindow *qif_win;
4009  gint component_id;
4010  SCM has_regex = scm_c_eval_string ("(defined? 'make-regexp)");
4011 
4012  if (scm_is_false(has_regex) == 1)
4013  {
4014  gnc_warning_dialog(NULL, _("QIF import requires guile with regex support."));
4015  return;
4016  }
4017 
4018  qif_win = g_new0 (QIFImportWindow, 1);
4019 
4020  /* pop up the QIF File Import dialog box */
4021  gnc_ui_qif_import_assistant_make (qif_win);
4022 
4023  component_id = gnc_register_gui_component (ASSISTANT_QIF_IMPORT_CM_CLASS,
4024  NULL, gnc_ui_qif_import_assistant_close_handler,
4025  qif_win);
4026 
4027  gnc_gui_component_watch_entity_type (component_id,
4028  GNC_ID_ACCOUNT,
4029  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
4030 
4031  gtk_widget_show_all (qif_win->window);
4032 
4033  gnc_window_adjust_for_screen (GTK_WINDOW(qif_win->window));
4034 }
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.
double gnc_numeric_to_double(gnc_numeric in)
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:573
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...