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