GnuCash 2.3.0
gnc-csv-import.c
Go to the documentation of this file.
00001 /*******************************************************************\
00002  * This program is free software; you can redistribute it and/or    *
00003  * modify it under the terms of the GNU General Public License as   *
00004  * published by the Free Software Foundation; either version 2 of   *
00005  * the License, or (at your option) any later version.              *
00006  *                                                                  *
00007  * This program is distributed in the hope that it will be useful,  *
00008  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00009  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00010  * GNU General Public License for more details.                     *
00011  *                                                                  *
00012  * You should have received a copy of the GNU General Public License*
00013  * along with this program; if not, contact:                        *
00014  *                                                                  *
00015  * Free Software Foundation           Voice:  +1-617-542-5942       *
00016  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00017  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00018 \********************************************************************/
00023 #include "config.h"
00024 
00025 #include <gtk/gtk.h>
00026 #include <glib/gi18n.h>
00027 #include <goffice/gtk/go-charmap-sel.h>
00028 
00029 #include "import-account-matcher.h"
00030 #include "import-main-matcher.h"
00031 
00032 #include "gnc-file.h"
00033 #include "gnc-ui-util.h"
00034 #include "gnc-glib-utils.h"
00035 #include "gnc-gui-query.h"
00036 #include "dialog-utils.h"
00037 
00038 #include "gnc-csv-import.h"
00039 #include "gnc-csv-model.h"
00040 #include "gnc-csv-gnumeric-popup.h"
00041 
00042 #define GCONF_SECTION "dialogs/import/csv"
00043 #define MIN_COL_WIDTH 70
00044 
00045 static QofLogModule log_module = GNC_MOD_IMPORT;
00046 
00050 enum SEP_BUTTON_TYPES {SEP_SPACE, SEP_TAB, SEP_COMMA, SEP_COLON, SEP_SEMICOLON, SEP_HYPHEN,
00051                        SEP_NUM_OF_TYPES
00052                       };
00053 
00057 typedef struct
00058 {
00059     GncCsvParseData* parse_data;       
00060     GtkDialog* dialog;
00061     GOCharmapSel* encselector;         
00062     GtkComboBox* date_format_combo;    
00063     GtkTreeView* treeview;             
00064     GtkTreeView* ctreeview;            
00065     GtkCheckButton* sep_buttons[SEP_NUM_OF_TYPES]; 
00066     GtkCheckButton* custom_cbutton;    
00067     GtkEntry* custom_entry;            
00068     GtkLabel* instructions_label;      
00069     GtkImage* instructions_image;      
00070     gboolean encoding_selected_called; 
00072     gboolean not_empty;                
00073     gboolean previewing_errors;        
00075     int code_encoding_calls;           
00079     gboolean approved;                 
00080     GtkWidget** treeview_buttons;      
00081     int longest_line;                  
00082     int fixed_context_col;             
00083     int fixed_context_dx;              
00085 } GncCsvPreview;
00086 
00087 static void gnc_csv_preview_update(GncCsvPreview* preview);
00088 
00096 static void sep_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
00097 {
00098     int i;
00099     char* stock_separator_characters[] = {" ", "\t", ",", ":", ";", "-"};
00100     GSList* checked_separators = NULL;
00101     GError* error;
00102 
00103     /* Add the corresponding characters to checked_separators for each
00104      * button that is checked. */
00105     for (i = 0; i < SEP_NUM_OF_TYPES; i++)
00106     {
00107         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(preview->sep_buttons[i])))
00108             checked_separators = g_slist_append(checked_separators, stock_separator_characters[i]);
00109     }
00110 
00111     /* Add the custom separator if the user checked its button. */
00112     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(preview->custom_cbutton)))
00113     {
00114         char* custom_sep = (char*)gtk_entry_get_text(preview->custom_entry);
00115         if (custom_sep[0] != '\0') /* Don't add a blank separator (bad things will happen!). */
00116             checked_separators = g_slist_append(checked_separators, custom_sep);
00117     }
00118 
00119     /* Set the parse options using the checked_separators list. */
00120     stf_parse_options_csv_set_separators(preview->parse_data->options, NULL, checked_separators);
00121     g_slist_free(checked_separators);
00122 
00123     /* Parse the data using the new options. We don't want to reguess
00124      * the column types because we want to leave the user's
00125      * configurations in tact. */
00126     if (gnc_csv_parse(preview->parse_data, FALSE, &error))
00127     {
00128         /* Warn the user there was a problem and try to undo what caused
00129          * the error. (This will cause a reparsing and ideally a usable
00130          * configuration.) */
00131         gnc_error_dialog(NULL, "Error in parsing");
00132         /* If the user changed the custom separator, erase that custom separator. */
00133         if (widget == GTK_WIDGET(preview->custom_entry))
00134         {
00135             gtk_entry_set_text(GTK_ENTRY(widget), "");
00136         }
00137         /* If the user checked a checkbutton, toggle that checkbutton back. */
00138         else
00139         {
00140             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
00141                                          !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
00142         }
00143         return;
00144     }
00145 
00146     /* If we parsed successfully, redisplay the data. */
00147     gnc_csv_preview_update(preview);
00148 }
00149 
00155 static void separated_or_fixed_selected(GtkToggleButton* csv_button, GncCsvPreview* preview)
00156 {
00157     GError* error = NULL;
00158     /* Set the parsing type correctly. */
00159     if (gtk_toggle_button_get_active(csv_button)) /* If we're in CSV mode ... */
00160     {
00161         stf_parse_options_set_type(preview->parse_data->options, PARSE_TYPE_CSV);
00162     }
00163     else /* If we're in fixed-width mode ... */
00164     {
00165         stf_parse_options_set_type(preview->parse_data->options, PARSE_TYPE_FIXED);
00166     }
00167 
00168     /* Reparse the data. */
00169     if (gnc_csv_parse(preview->parse_data, FALSE, &error))
00170     {
00171         /* Show an error dialog explaining the problem. */
00172         gnc_error_dialog(NULL, "%s", error->message);
00173         return;
00174     }
00175 
00176     /* Show the new data. */
00177     gnc_csv_preview_update(preview);
00178 }
00179 
00187 static void encoding_selected(GOCharmapSel* selector, const char* encoding,
00188                               GncCsvPreview* preview)
00189 {
00190     /* This gets called twice everytime a new encoding is selected. The
00191      * second call actually passes the correct data; thus, we only do
00192      * something the second time this is called. */
00193 
00194     /* Prevent code-caused calls of this function from having an impact. */
00195     if (preview->code_encoding_calls > 0)
00196     {
00197         preview->code_encoding_calls--;
00198         return;
00199     }
00200 
00201     /* If this is the second time the function is called ... */
00202     if (preview->encoding_selected_called)
00203     {
00204         const char* previous_encoding = preview->parse_data->encoding;
00205         GError* error = NULL;
00206         /* Try converting the new encoding and reparsing. */
00207         if (gnc_csv_convert_encoding(preview->parse_data, encoding, &error) ||
00208                 gnc_csv_parse(preview->parse_data, FALSE, &error))
00209         {
00210             /* If it fails, change back to the old encoding. */
00211             gnc_error_dialog(NULL, "%s", _("Invalid encoding selected"));
00212             preview->encoding_selected_called = FALSE;
00213             go_charmap_sel_set_encoding(selector, previous_encoding);
00214             return;
00215         }
00216 
00217         gnc_csv_preview_update(preview);
00218         preview->encoding_selected_called = FALSE;
00219     }
00220     else /* If this is the first call of the function ... */
00221     {
00222         preview->encoding_selected_called = TRUE; /* ... set the flag and wait for the next call. */
00223     }
00224 }
00225 
00230 static void date_format_selected(GtkComboBox* format_selector, GncCsvPreview* preview)
00231 {
00232     preview->parse_data->date_format = gtk_combo_box_get_active(format_selector);
00233 }
00234 
00241 static void ok_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
00242 {
00243     /* Shorten the column_types identifier. */
00244     GArray* column_types = preview->parse_data->column_types;
00245     int i, ncols = column_types->len; /* ncols is the number of columns in the data. */
00246     /* store contains the actual strings appearing in the column types treeview. */
00247     GtkTreeModel* store = gtk_tree_view_get_model(preview->ctreeview);
00248     GtkTreeIter iter;
00249     /* Get an iterator for the first (and only) row. */
00250     gtk_tree_model_get_iter_first(store, &iter);
00251 
00252     /* Go through each of the columns. */
00253     for (i = 0; i < ncols; i++)
00254     {
00255         int type; /* The column type contained in this column. */
00256         gchar* contents = NULL; /* The column type string in this column. */
00257         /* Get the type string first. (store is arranged so that every two
00258          * columns is a pair of the model used for the combobox and the
00259          * string that appears, so that store looks like:
00260          * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
00261         gtk_tree_model_get(store, &iter, 2 * i + 1, &contents, -1);
00262 
00263         /* Go through each column type until ... */
00264         for (type = 0; type < GNC_CSV_NUM_COL_TYPES; type++)
00265         {
00266             /* ... we find one that matches with what's in the column. */
00267             if (!safe_strcmp(contents, _(gnc_csv_column_type_strs[type])))
00268             {
00269                 /* Set the column_types array appropriately and quit. */
00270                 column_types->data[i] = type;
00271                 break;
00272             }
00273         }
00274         /* Free the type string created by gtk_tree_model_get() */
00275         g_free(contents);
00276     }
00277 
00278     /* Close the dialog. */
00279     gtk_widget_hide((GtkWidget*)(preview->dialog));
00280     preview->approved = TRUE; /* The user has wants to do the import. */
00281 }
00282 
00288 static void cancel_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
00289 {
00290     gtk_widget_hide((GtkWidget*)(preview->dialog));
00291 }
00292 
00300 static void treeview_resized(GtkWidget* widget, GtkAllocation* allocation, GncCsvPreview* preview)
00301 {
00302     /* ncols is the number of columns in the data. */
00303     int i, ncols = preview->parse_data->column_types->len;
00304 
00305     /* Go through each column except for the last. (We don't want to set
00306      * the width of the last column because the user won't be able to
00307      * shrink the dialog back if it's expanded.) */
00308     for (i = 0; i < ncols - 1; i++)
00309     {
00310         gint col_width; /* The width of the column in preview->treeview. */
00311         GtkTreeViewColumn* pcol;
00312         GtkTreeViewColumn* ccol; /* The corresponding column in preview->ctreeview. */
00313 
00314         /* Get the width. */
00315         col_width = gtk_tree_view_column_get_width(gtk_tree_view_get_column(preview->treeview, i));
00316         /* Set the minumum width for a column so that drop down selector can be seen. */
00317         if (col_width < MIN_COL_WIDTH)
00318         {
00319             col_width = MIN_COL_WIDTH;
00320         }
00321         pcol = gtk_tree_view_get_column(preview->treeview, i);
00322         gtk_tree_view_column_set_min_width(pcol, col_width);
00323         /* Set ccol's width the same. */
00324         ccol = gtk_tree_view_get_column(preview->ctreeview, i);
00325         gtk_tree_view_column_set_min_width(ccol, col_width);
00326         gtk_tree_view_column_set_max_width(ccol, col_width);
00327     }
00328 }
00329 
00339 static void column_type_changed(GtkCellRenderer* renderer, gchar* path,
00340                                 GtkTreeIter* new_text_iter, GncCsvPreview* preview)
00341 {
00342     /* ncols is the number of columns in the data. */
00343     int i, ncols = preview->parse_data->column_types->len;
00344     /* store has the actual strings that appear in preview->ctreeview. */
00345     GtkTreeModel* store = gtk_tree_view_get_model(preview->ctreeview);
00346     GtkTreeModel* model;
00347     gint textColumn;
00348     GtkTreeIter iter;
00349     gchar* new_text;
00350 
00351     /* Get the new text */
00352     g_object_get(renderer, "model", &model, "text-column", &textColumn, NULL);
00353     gtk_tree_model_get(model, new_text_iter, textColumn, &new_text, -1);
00354 
00355     /* Get an iterator for the first (and only) row. */
00356     gtk_tree_model_get_iter_first(store, &iter);
00357 
00358     /* Go through each column. */
00359     for (i = 0; i < ncols; i++)
00360     {
00361         /* We need all this stuff so that we can find out whether or not
00362          * this was the column that was changed. */
00363         GtkCellRenderer* col_renderer; /* The renderer for this column. */
00364         /* The column in the treeview we are looking at */
00365         GtkTreeViewColumn* col = gtk_tree_view_get_column(preview->ctreeview, i);
00366         /* The list of renderers for col */
00367         GList* rend_list = gtk_tree_view_column_get_cell_renderers(col);
00368         /* rend_list has only one entry, which we put in col_renderer. */
00369         col_renderer = rend_list->data;
00370         g_list_free(rend_list);
00371 
00372         /* If this is not the column that was changed ... */
00373         if (col_renderer != renderer)
00374         {
00375             /* The string that appears in the column */
00376             gchar* contents = NULL;
00377             /* Get the type string. (store is arranged so that every two
00378              * columns is a pair of the model used for the combobox and the
00379              * string that appears, so that store looks like:
00380              * model 0, string 0, model 1, string 1, ..., model ncols, string ncols. */
00381             gtk_tree_model_get(store, &iter, 2 * i + 1, &contents, -1);
00382             /* If this column has the same string that the user selected ... */
00383             if (!safe_strcmp(contents, new_text))
00384             {
00385                 /* ... set this column to the "None" type. (We can't allow duplicate types.) */
00386                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, 2 * i + 1,
00387                                    _(gnc_csv_column_type_strs[GNC_CSV_NONE]), -1);
00388             }
00389             g_free(contents);
00390         }
00391         else /* If this is the column that was changed ... */
00392         {
00393             /* Set the text for this column to what the user selected. (See
00394              * comment above "Get the type string. ..." for why we set
00395              * column 2*i+1 in store.) */
00396             gtk_list_store_set(GTK_LIST_STORE(store), &iter, 2 * i + 1, new_text, -1);
00397         }
00398     }
00399 }
00400 
00404 static GncCsvPreview* gnc_csv_preview_new()
00405 {
00406     int i;
00407     GncCsvPreview* preview = g_new(GncCsvPreview, 1);
00408     GtkWidget *ok_button, *cancel_button, *csv_button;
00409     GtkContainer* date_format_container;
00410     GtkBuilder *builder;
00411     /* The names in the glade builder file for the sep buttons. */
00412     char* sep_button_names[] = {"space_cbutton",
00413                                 "tab_cbutton",
00414                                 "comma_cbutton",
00415                                 "colon_cbutton",
00416                                 "semicolon_cbutton",
00417                                 "hyphen_cbutton"
00418                                };
00419     /* The table containing preview->encselector and the separator configuration widgets */
00420     GtkTable* enctable;
00421     PangoContext* context; /* Used to set a monotype font on preview->treeview */
00422 
00423     preview->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
00424     /* Connect the selector to the encoding_selected event handler. */
00425     g_signal_connect(G_OBJECT(preview->encselector), "charmap_changed",
00426                      G_CALLBACK(encoding_selected), (gpointer)preview);
00427 
00428     /* Load the Glade builder file. */
00429     builder = gtk_builder_new();
00430     gnc_builder_add_from_file (builder, "gnc-csv-import.glade", "preview-dialog");
00431 
00432     /* Load the dialog. */
00433     preview->dialog = GTK_DIALOG(gtk_builder_get_object (builder, "preview-dialog"));
00434 
00435     /* Load the separator buttons from the glade builder file into the
00436      * preview->sep_buttons array. */
00437     for (i = 0; i < SEP_NUM_OF_TYPES; i++)
00438     {
00439         preview->sep_buttons[i]
00440         = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, sep_button_names[i]));
00441         /* Connect them to the sep_button_clicked event handler. */
00442         g_signal_connect(G_OBJECT(preview->sep_buttons[i]), "toggled",
00443                          G_CALLBACK(sep_button_clicked), (gpointer)preview);
00444     }
00445 
00446     /* Load and connect the custom separator checkbutton in the same way
00447      * as the other separator buttons. */
00448     preview->custom_cbutton
00449     = (GtkCheckButton*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_cbutton"));
00450     g_signal_connect(G_OBJECT(preview->custom_cbutton), "clicked",
00451                      G_CALLBACK(sep_button_clicked), (gpointer)preview);
00452 
00453     /* Load the entry for the custom separator entry. Connect it to the
00454      * sep_button_clicked event handler as well. */
00455     preview->custom_entry = (GtkEntry*)GTK_WIDGET(gtk_builder_get_object (builder, "custom_entry"));
00456     g_signal_connect(G_OBJECT(preview->custom_entry), "changed",
00457                      G_CALLBACK(sep_button_clicked), (gpointer)preview);
00458 
00459     /* Get the table from the Glade builder file. */
00460     enctable = GTK_TABLE(gtk_builder_get_object (builder, "enctable"));
00461     /* Put the selector in at the top. */
00462     gtk_table_attach_defaults(enctable, GTK_WIDGET(preview->encselector), 1, 2, 0, 1);
00463     /* Show the table in all its glory. */
00464     gtk_widget_show_all(GTK_WIDGET(enctable));
00465 
00466     /* The instructions label and image */
00467     preview->instructions_label = GTK_LABEL(gtk_builder_get_object (builder, "instructions_label"));
00468     preview->instructions_image = GTK_IMAGE(gtk_builder_get_object (builder, "instructions_image"));
00469 
00470     /* Add in the date format combo box and hook it up to an event handler. */
00471     preview->date_format_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
00472     for (i = 0; i < num_date_formats; i++)
00473     {
00474         gtk_combo_box_append_text(preview->date_format_combo, _(date_format_user[i]));
00475     }
00476     gtk_combo_box_set_active(preview->date_format_combo, 0);
00477     g_signal_connect(G_OBJECT(preview->date_format_combo), "changed",
00478                      G_CALLBACK(date_format_selected), (gpointer)preview);
00479 
00480     /* Add it to the dialog. */
00481     date_format_container = GTK_CONTAINER(gtk_builder_get_object (builder, "date_format_container"));
00482     gtk_container_add(date_format_container, GTK_WIDGET(preview->date_format_combo));
00483     gtk_widget_show_all(GTK_WIDGET(date_format_container));
00484 
00485     /* Connect the "OK" and "Cancel" buttons to their event handlers. */
00486     ok_button = GTK_WIDGET(gtk_builder_get_object (builder, "ok_button"));
00487     g_signal_connect(G_OBJECT(ok_button), "clicked",
00488                      G_CALLBACK(ok_button_clicked), (gpointer)preview);
00489 
00490     cancel_button = GTK_WIDGET(gtk_builder_get_object (builder, "cancel_button"));
00491     g_signal_connect(G_OBJECT(cancel_button), "clicked",
00492                      G_CALLBACK(cancel_button_clicked), (gpointer)preview);
00493 
00494     /* Connect the CSV/Fixed-Width radio button event handler. */
00495     csv_button = GTK_WIDGET(gtk_builder_get_object (builder, "csv_button"));
00496     g_signal_connect(csv_button, "toggled",
00497                      G_CALLBACK(separated_or_fixed_selected), (gpointer)preview);
00498 
00499     /* Load the data treeview and connect it to its resizing event handler. */
00500     preview->treeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
00501     g_signal_connect(G_OBJECT(preview->treeview), "size-allocate",
00502                      G_CALLBACK(treeview_resized), (gpointer)preview);
00503     context = gtk_widget_create_pango_context(GTK_WIDGET(preview->treeview));
00504 
00505     /* Load the column type treeview. */
00506     preview->ctreeview = (GtkTreeView*)GTK_WIDGET(gtk_builder_get_object (builder, "ctreeview"));
00507 
00508     /* This is TRUE only after encoding_selected is called, so we must
00509      * set it initially to FALSE. */
00510     preview->encoding_selected_called = FALSE;
00511 
00512     /* It is empty at first. */
00513     preview->not_empty = FALSE;
00514 
00515     g_object_unref(G_OBJECT(builder));
00516 
00517     return preview;
00518 }
00519 
00524 static void gnc_csv_preview_free(GncCsvPreview* preview)
00525 {
00526     g_free(preview);
00527 }
00528 
00534 static GtkCellRenderer* gnc_csv_preview_get_cell_renderer(GncCsvPreview* preview, int col)
00535 {
00536     GList* renderers = gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(preview->treeview, col));
00537     GtkCellRenderer* cell = GTK_CELL_RENDERER(renderers->data);
00538     g_list_free(renderers);
00539     return cell;
00540 }
00541 
00542 /* The following is code copied from Gnumeric 1.7.8 licensed under the
00543  * GNU General Public License version 2. It is from the file
00544  * gnumeric/src/dialogs/dialog-stf-fixed-page.c, and it has been
00545  * modified slightly to work within GnuCash. */
00546 
00547 /* ---- Beginning of Gnumeric Code ---- */
00548 
00549 /*
00550  * Copyright 2001 Almer S. Tigelaar <almer@gnome.org>
00551  * Copyright 2003 Morten Welinder <terra@gnome.org>
00552  *
00553  * This program is free software; you can redistribute it and/or modify
00554  * it under the terms of the GNU General Public License as published by
00555  * the Free Software Foundation; either version 2 of the License, or
00556  * (at your option) any later version.
00557  *
00558  * This program is distributed in the hope that it will be useful,
00559  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00560  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00561  * GNU General Public License for more details.
00562  *
00563  * You should have received a copy of the GNU General Public License
00564  * along with this program; if not, write to the Free Software
00565  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00566  */
00567 
00568 enum
00569 {
00570     CONTEXT_STF_IMPORT_MERGE_LEFT = 1,
00571     CONTEXT_STF_IMPORT_MERGE_RIGHT = 2,
00572     CONTEXT_STF_IMPORT_SPLIT = 3,
00573     CONTEXT_STF_IMPORT_WIDEN = 4,
00574     CONTEXT_STF_IMPORT_NARROW = 5
00575 };
00576 
00577 static GnumericPopupMenuElement const popup_elements[] =
00578 {
00579     {
00580         N_("Merge with column on _left"), GTK_STOCK_REMOVE,
00581         0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT
00582     },
00583     {
00584         N_("Merge with column on _right"), GTK_STOCK_REMOVE,
00585         0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT
00586     },
00587     { "", NULL, 0, 0, 0 },
00588     {
00589         N_("_Split this column"), NULL,
00590         0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT
00591     },
00592     { "", NULL, 0, 0, 0 },
00593     {
00594         N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
00595         0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN
00596     },
00597     {
00598         N_("_Narrow this column"), GTK_STOCK_GO_BACK,
00599         0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW
00600     },
00601     { NULL, NULL, 0, 0, 0 },
00602 };
00603 
00604 static gboolean
00605 make_new_column (GncCsvPreview *preview, int col, int dx, gboolean test_only)
00606 {
00607     PangoLayout *layout;
00608     PangoFontDescription *font_desc;
00609     int charindex, width;
00610     GtkCellRenderer *cell =     gnc_csv_preview_get_cell_renderer(preview, col);
00611     int colstart, colend;
00612     GError* error = NULL;
00613 
00614     colstart = (col == 0)
00615                ? 0
00616                : stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col - 1);
00617     colend = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
00618 
00619     g_object_get (G_OBJECT (cell), "font_desc", &font_desc, NULL);
00620     layout = gtk_widget_create_pango_layout (GTK_WIDGET (preview->treeview), "x");
00621     pango_layout_set_font_description (layout, font_desc);
00622     pango_layout_get_pixel_size (layout, &width, NULL);
00623     if (width < 1) width = 1;
00624     charindex = colstart + (dx + width / 2) / width;
00625     g_object_unref (layout);
00626     pango_font_description_free (font_desc);
00627 
00628     if (charindex <= colstart || (colend != -1 && charindex >= colend))
00629         return FALSE;
00630 
00631     if (!test_only)
00632     {
00633         stf_parse_options_fixed_splitpositions_add (preview->parse_data->options, charindex);
00634         if (gnc_csv_parse(preview->parse_data, FALSE, &error))
00635         {
00636             gnc_error_dialog(NULL, "%s", error->message);
00637             return FALSE;
00638         }
00639         gnc_csv_preview_update (preview);
00640     }
00641 
00642     return TRUE;
00643 }
00644 
00645 
00646 static gboolean
00647 widen_column (GncCsvPreview *preview, int col, gboolean test_only)
00648 {
00649     int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
00650     int nextstart, nextnextstart;
00651     GError* error = NULL;
00652 
00653     if (col >= colcount - 1)
00654         return FALSE;
00655 
00656     nextstart = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
00657 
00658     nextnextstart = (col == colcount - 2)
00659                     ? preview->longest_line
00660                     : stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col + 1);
00661 
00662     if (nextstart + 1 >= nextnextstart)
00663         return FALSE;
00664 
00665     if (!test_only)
00666     {
00667         stf_parse_options_fixed_splitpositions_remove (preview->parse_data->options, nextstart);
00668         stf_parse_options_fixed_splitpositions_add (preview->parse_data->options, nextstart + 1);
00669         if (gnc_csv_parse(preview->parse_data, FALSE, &error))
00670         {
00671             gnc_error_dialog(NULL, "%s", error->message);
00672             return FALSE;
00673         }
00674         gnc_csv_preview_update (preview);
00675     }
00676     return TRUE;
00677 }
00678 
00679 static gboolean
00680 narrow_column (GncCsvPreview *preview, int col, gboolean test_only)
00681 {
00682     int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
00683     int thisstart, nextstart;
00684     GError* error = NULL;
00685 
00686     if (col >= colcount - 1)
00687         return FALSE;
00688 
00689     thisstart = (col == 0)
00690                 ? 0
00691                 : stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col - 1);
00692     nextstart = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
00693 
00694     if (nextstart - 1 <= thisstart)
00695         return FALSE;
00696 
00697     if (!test_only)
00698     {
00699         stf_parse_options_fixed_splitpositions_remove (preview->parse_data->options, nextstart);
00700         stf_parse_options_fixed_splitpositions_add (preview->parse_data->options, nextstart - 1);
00701         if (gnc_csv_parse(preview->parse_data, FALSE, &error))
00702         {
00703             gnc_error_dialog(NULL, "%s", error->message);
00704             return FALSE;
00705         }
00706         gnc_csv_preview_update (preview);
00707     }
00708     return TRUE;
00709 }
00710 
00711 static gboolean
00712 delete_column (GncCsvPreview *preview, int col, gboolean test_only)
00713 {
00714     GError* error = NULL;
00715     int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
00716     if (col < 0 || col >= colcount - 1)
00717         return FALSE;
00718 
00719     if (!test_only)
00720     {
00721         int nextstart = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
00722         stf_parse_options_fixed_splitpositions_remove (preview->parse_data->options, nextstart);
00723         if (gnc_csv_parse(preview->parse_data, FALSE, &error))
00724         {
00725             gnc_error_dialog(NULL, "%s", error->message);
00726             return FALSE;
00727         }
00728         gnc_csv_preview_update (preview);
00729     }
00730     return TRUE;
00731 }
00732 
00733 static void
00734 select_column (GncCsvPreview *preview, int col)
00735 {
00736     GError* error = NULL;
00737     int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
00738     GtkTreeViewColumn *column;
00739 
00740     if (col < 0 || col >= colcount)
00741         return;
00742 
00743     column = gtk_tree_view_get_column (preview->treeview, col);
00744     gtk_widget_grab_focus (column->button);
00745 }
00746 
00747 static gboolean
00748 fixed_context_menu_handler (GnumericPopupMenuElement const *element,
00749                             gpointer user_data)
00750 {
00751     GncCsvPreview *preview = user_data;
00752     int col = preview->fixed_context_col;
00753 
00754     switch (element->index)
00755     {
00756     case CONTEXT_STF_IMPORT_MERGE_LEFT:
00757         delete_column (preview, col - 1, FALSE);
00758         break;
00759     case CONTEXT_STF_IMPORT_MERGE_RIGHT:
00760         delete_column (preview, col, FALSE);
00761         break;
00762     case CONTEXT_STF_IMPORT_SPLIT:
00763         make_new_column (preview, col, preview->fixed_context_dx, FALSE);
00764         break;
00765     case CONTEXT_STF_IMPORT_WIDEN:
00766         widen_column (preview, col, FALSE);
00767         break;
00768     case CONTEXT_STF_IMPORT_NARROW:
00769         narrow_column (preview, col, FALSE);
00770         break;
00771     default:
00772         ; /* Nothing */
00773     }
00774     return TRUE;
00775 }
00776 
00777 static void
00778 fixed_context_menu (GncCsvPreview *preview, GdkEventButton *event,
00779                     int col, int dx)
00780 {
00781     int sensitivity_filter = 0;
00782 
00783     preview->fixed_context_col = col;
00784     preview->fixed_context_dx = dx;
00785 
00786     if (!delete_column (preview, col - 1, TRUE))
00787         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT);
00788     if (!delete_column (preview, col, TRUE))
00789         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT);
00790     if (!make_new_column (preview, col, dx, TRUE))
00791         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT);
00792     if (!widen_column (preview, col, TRUE))
00793         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN);
00794     if (!narrow_column (preview, col, TRUE))
00795         sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW);
00796 
00797     select_column (preview, col);
00798     gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler,
00799                                 preview, 0,
00800                                 sensitivity_filter, event);
00801 }
00802 
00803 /* ---- End of Gnumeric Code ---- */
00804 
00812 static void header_button_press_handler(GtkWidget* button, GdkEventButton* event,
00813                                         GncCsvPreview* preview)
00814 {
00815     /* col is the number of the column that was clicked, and offset is
00816        to correct for the indentation of button. */
00817     int i, col = 0, offset = GTK_BIN(button)->child->allocation.x - button->allocation.x,
00818            ncols = preview->parse_data->column_types->len;
00819     /* Find the column that was clicked. */
00820     for (i = 0; i < ncols; i++)
00821     {
00822         if (preview->treeview_buttons[i] == button)
00823         {
00824             col = i;
00825             break;
00826         }
00827     }
00828 
00829     /* Don't let the user affect the last column if it has error messages. */
00830     if (preview->parse_data->orig_max_row < ncols && ncols - col == 1)
00831     {
00832         return;
00833     }
00834 
00835     /* Double clicks can split columns. */
00836     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
00837     {
00838         make_new_column(preview, col, (int)event->x - offset, FALSE);
00839     }
00840     /* Right clicking brings up a context menu. */
00841     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
00842     {
00843         fixed_context_menu(preview, event, col, (int)event->x - offset);
00844     }
00845 }
00846 
00847 /* Loads the preview's data into its data treeview. notEmpty is TRUE
00848  * when the data treeview already contains data, FALSE otherwise
00849  * (e.g. the first time this function is called on a preview).
00850  * @param preview The data being previewed
00851  * @param notEmpty Whether this function has been called before or not
00852  */
00853 static void gnc_csv_preview_update(GncCsvPreview* preview)
00854 {
00855     /* store has the data from the file being imported. cstores is an
00856      * array of stores that hold the combo box entries for each
00857      * column. ctstore contains both pointers to models in cstore and
00858      * the actual text that appears in preview->ctreeview. */
00859     GtkListStore *store, **cstores, *ctstore;
00860     GtkTreeIter iter;
00861     /* ncols is the number of columns in the file data. */
00862     int i, j, ncols = preview->parse_data->column_types->len,
00863               max_str_len = preview->parse_data->file_str.end - preview->parse_data->file_str.begin;
00864 
00865     /* store contains only strings. */
00866     GType* types = g_new(GType, 2 * ncols);
00867     for (i = 0; i < ncols; i++)
00868         types[i] = G_TYPE_STRING;
00869     store = gtk_list_store_newv(ncols, types);
00870 
00871     /* ctstore is arranged as follows:
00872      * model 0, text 0, model 1, text 1, ..., model ncols, text ncols. */
00873     for (i = 0; i < 2 * ncols; i += 2)
00874     {
00875         types[i] = GTK_TYPE_TREE_MODEL;
00876         types[i+1] = G_TYPE_STRING;
00877     }
00878     ctstore = gtk_list_store_newv(2 * ncols, types);
00879 
00880     g_free(types);
00881 
00882     /* Each element in cstores is a single column model. */
00883     cstores = g_new(GtkListStore*, ncols);
00884     for (i = 0; i < ncols; i++)
00885     {
00886         cstores[i] = gtk_list_store_new(1, G_TYPE_STRING);
00887         /* Add all of the possible entries to the combo box. */
00888         for (j = 0; j < GNC_CSV_NUM_COL_TYPES; j++)
00889         {
00890             gtk_list_store_append(cstores[i], &iter);
00891             gtk_list_store_set(cstores[i], &iter, 0, _(gnc_csv_column_type_strs[j]), -1);
00892         }
00893     }
00894 
00895     if (preview->not_empty)
00896     {
00897         GList *children, *children_begin;
00898         GList *tv_columns, *tv_columns_begin, *ctv_columns, *ctv_columns_begin;
00899         tv_columns = tv_columns_begin = gtk_tree_view_get_columns(preview->treeview);
00900         ctv_columns = ctv_columns_begin = gtk_tree_view_get_columns(preview->ctreeview);
00901         /* Clear out exisiting columns in preview->treeview. */
00902         while (tv_columns != NULL)
00903         {
00904             gtk_tree_view_remove_column(preview->treeview, GTK_TREE_VIEW_COLUMN(tv_columns->data));
00905             tv_columns = g_list_next(tv_columns);
00906         }
00907         /* Do the same in preview->ctreeview. */
00908         while (ctv_columns != NULL)
00909         {
00910             gtk_tree_view_remove_column(preview->ctreeview, GTK_TREE_VIEW_COLUMN(ctv_columns->data));
00911             ctv_columns = g_list_next(ctv_columns);
00912         }
00913         g_list_free(tv_columns_begin);
00914         g_list_free(ctv_columns_begin);
00915         g_free(preview->treeview_buttons);
00916     }
00917 
00918     /* Fill the data treeview with data from the file. */
00919     /* Also, update the longest line value within the following loop (whichever is executed). */
00920     preview->longest_line = 0;
00921     if (preview->previewing_errors) /* If we are showing only errors ... */
00922     {
00923         /* ... only pick rows that are in preview->error_lines. */
00924         GList* error_lines = preview->parse_data->error_lines;
00925         while (error_lines != NULL)
00926         {
00927             int this_line_length = 0;
00928             i = GPOINTER_TO_INT(error_lines->data);
00929             gtk_list_store_append(store, &iter);
00930             for (j = 0; j < ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->len; j++)
00931             {
00932                 /* Add this cell's length to the row's length and set the value of the list store. */
00933                 gchar* cell_string = (gchar*)((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j];
00934                 this_line_length += g_utf8_strlen(cell_string, max_str_len);
00935                 gtk_list_store_set(store, &iter, j, cell_string, -1);
00936             }
00937 
00938             if (this_line_length > preview->longest_line)
00939                 preview->longest_line = this_line_length;
00940 
00941             error_lines = g_list_next(error_lines);
00942         }
00943     }
00944     else /* Otherwise, put in all of the data. */
00945     {
00946         for (i = 0; i < preview->parse_data->orig_lines->len; i++)
00947         {
00948             int this_line_length = 0;
00949             gtk_list_store_append(store, &iter);
00950             for (j = 0; j < ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->len; j++)
00951             {
00952                 /* Add this cell's length to the row's length and set the value of the list store. */
00953                 gchar* cell_string = (gchar*)((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j];
00954                 this_line_length += g_utf8_strlen(cell_string, max_str_len);
00955                 gtk_list_store_set(store, &iter, j, cell_string, -1);
00956             }
00957 
00958             if (this_line_length > preview->longest_line)
00959                 preview->longest_line = this_line_length;
00960         }
00961     }
00962 
00963     /* Set all the column types to what's in the parse data. */
00964     gtk_list_store_append(ctstore, &iter);
00965     for (i = 0; i < ncols; i++)
00966     {
00967         gtk_list_store_set(ctstore, &iter, 2 * i, cstores[i], 2 * i + 1,
00968                            _(gnc_csv_column_type_strs[(int)(preview->parse_data->column_types->data[i])]),
00969                            -1);
00970     }
00971 
00972     preview->treeview_buttons = g_new(GtkWidget*, ncols);
00973     /* Insert columns into the data and column type treeviews. */
00974     for (i = 0; i < ncols; i++)
00975     {
00976         GtkTreeViewColumn* col; /* The column we add to preview->treeview. */
00977         /* Create renderers for the data treeview (renderer) and the
00978          * column type treeview (crenderer). */
00979         GtkCellRenderer* renderer = gtk_cell_renderer_text_new(),
00980                          *crenderer = gtk_cell_renderer_combo_new();
00981         /* We want a monospace font for the data in case of fixed-width data. */
00982         g_object_set(G_OBJECT(renderer), "family", "monospace", NULL);
00983         /* We are using cstores for the combo box entries, and we don't
00984          * want the user to be able to manually enter their own column
00985          * types. */
00986         g_object_set(G_OBJECT(crenderer), "model", cstores[i], "text-column", 0,
00987                      "editable", TRUE, "has-entry", FALSE, NULL);
00988         g_signal_connect(G_OBJECT(crenderer), "changed",
00989                          G_CALLBACK(column_type_changed), (gpointer)preview);
00990 
00991         /* Add a single column for the treeview. */
00992         col = gtk_tree_view_column_new_with_attributes("", renderer, "text", i, NULL);
00993         gtk_tree_view_insert_column(preview->treeview, col, -1);
00994         /* Enable resizing of the columns. */
00995         gtk_tree_view_column_set_resizable(col, TRUE);
00996         /* Use the alternating model and text entries from ctstore in
00997          * preview->ctreeview. */
00998         gtk_tree_view_insert_column_with_attributes(preview->ctreeview,
00999                 -1, "", crenderer, "model", 2 * i,
01000                 "text", 2 * i + 1, NULL);
01001 
01002         /* We need to allow clicking on the column headers for fixed-width
01003          * column splitting and merging. */
01004         g_object_set(G_OBJECT(col), "clickable", TRUE, NULL);
01005         g_signal_connect(G_OBJECT(col->button), "button_press_event",
01006                          G_CALLBACK(header_button_press_handler), (gpointer)preview);
01007         preview->treeview_buttons[i] = col->button;
01008     }
01009 
01010     /* Set the treeviews to use the models. */
01011     gtk_tree_view_set_model(preview->treeview, GTK_TREE_MODEL(store));
01012     gtk_tree_view_set_model(preview->ctreeview, GTK_TREE_MODEL(ctstore));
01013 
01014     /* Free the memory for the stores. */
01015     g_object_unref(GTK_TREE_MODEL(store));
01016     g_object_unref(GTK_TREE_MODEL(ctstore));
01017     for (i = 0; i < ncols; i++)
01018         g_object_unref(GTK_TREE_MODEL(cstores[i]));
01019 
01020     /* Make the things actually appear. */
01021     gtk_widget_show_all(GTK_WIDGET(preview->treeview));
01022     gtk_widget_show_all(GTK_WIDGET(preview->ctreeview));
01023 
01024     /* Set the encoding selector to the right encoding. */
01025     preview->code_encoding_calls = 2;
01026     go_charmap_sel_set_encoding(preview->encselector, preview->parse_data->encoding);
01027 
01028     /* Set the date format to what's in the combo box (since we don't
01029      * necessarily know if this will always be the same). */
01030     preview->parse_data->date_format = gtk_combo_box_get_active(preview->date_format_combo);
01031 
01032     /* It's now been filled with some stuff. */
01033     preview->not_empty = TRUE;
01034 }
01035 
01044 static int gnc_csv_preview(GncCsvPreview* preview, GncCsvParseData* parse_data)
01045 {
01046     /* Set the preview's parse_data to the one we're getting passed. */
01047     preview->parse_data = parse_data;
01048     preview->previewing_errors = FALSE; /* We're looking at all the data. */
01049     preview->approved = FALSE; /* This is FALSE until the user clicks "OK". */
01050 
01051     /* Load the data into the treeview. (This is the first time we've
01052      * called gnc_csv_preview_update on this preview, so we use
01053      * FALSE.) */
01054     gnc_csv_preview_update(preview);
01055     /* Wait until the user clicks "OK" or "Cancel". */
01056     gtk_dialog_run(GTK_DIALOG(preview->dialog));
01057 
01058     if (preview->approved)
01059         return 0;
01060     else
01061         return 1;
01062 }
01063 
01071 /* TODO Let the user manually edit cells' data? */
01072 static int gnc_csv_preview_errors(GncCsvPreview* preview)
01073 {
01074     gchar* name;
01075     GtkIconSize size;
01076     GtkTreeViewColumn* last_col;
01077 
01078     gtk_image_get_stock(preview->instructions_image, &name, &size);
01079     gtk_image_set_from_stock(preview->instructions_image, GTK_STOCK_DIALOG_ERROR, size);
01080     gtk_label_set_text(preview->instructions_label,
01081                        _("The rows displayed below had errors. You can attempt to correct these errors by changing the configuration."));
01082     gtk_widget_show(GTK_WIDGET(preview->instructions_image));
01083     gtk_widget_show(GTK_WIDGET(preview->instructions_label));
01084 
01085     preview->previewing_errors = TRUE;
01086     preview->approved = FALSE; /* This is FALSE until the user clicks "OK". */
01087 
01088     /* Wait until the user clicks "OK" or "Cancel". */
01089     gnc_csv_preview_update(preview);
01090 
01091     /* Set the last column to have the header "Errors" so that the user
01092      * doesn't find the extra column confusing. */
01093     last_col = gtk_tree_view_get_column(preview->treeview,
01094                                         preview->parse_data->column_types->len - 1);
01095     gtk_tree_view_column_set_title(last_col, _("Errors"));
01096 
01097     gtk_dialog_run(GTK_DIALOG(preview->dialog));
01098 
01099     if (preview->approved)
01100         return 0;
01101     else
01102         return 1;
01103 }
01104 
01106 void gnc_file_csv_import(void)
01107 {
01108     /* The name of the file the user selected. */
01109     char* selected_filename;
01110     /* The default directory for the user to select files. */
01111     char* default_dir = gnc_get_default_directory(GCONF_SECTION);
01112     /* The generic GUI for importing transactions. */
01113     GNCImportMainMatcher* gnc_csv_importer_gui = NULL;
01114 
01115     /* Let the user select a file. */
01116     selected_filename = gnc_file_dialog(_("Select an CSV/Fixed-Width file to import"),
01117                                         NULL, default_dir, GNC_FILE_DIALOG_IMPORT);
01118     g_free(default_dir); /* We don't need default_dir anymore. */
01119 
01120     /* If the user actually selected a file ... */
01121     if (selected_filename != NULL)
01122     {
01123         int i, user_canceled = 0;
01124         Account* account; /* The account the user will select */
01125         GError* error = NULL;
01126         GList* transactions; /* A list of the transactions we create */
01127         GncCsvParseData* parse_data;
01128         GncCsvPreview* preview;
01129 
01130         /* Remember the directory of the selected file as the default. */
01131         default_dir = g_path_get_dirname(selected_filename);
01132         gnc_set_default_directory(GCONF_SECTION, default_dir);
01133         g_free(default_dir);
01134 
01135         /* Load the file into parse_data. */
01136         parse_data = gnc_csv_new_parse_data();
01137         if (gnc_csv_load_file(parse_data, selected_filename, &error))
01138         {
01139             /* If we couldn't load the file ... */
01140             gnc_error_dialog(NULL, "%s", error->message);
01141             if (error->code == GNC_CSV_FILE_OPEN_ERR)
01142             {
01143                 gnc_csv_parse_data_free(parse_data);
01144                 g_free(selected_filename);
01145                 return;
01146             }
01147             /* If we couldn't guess the encoding, we are content with just
01148              * displaying an error message and move on with a blank
01149              * display. */
01150         }
01151         /* Parse the data. */
01152         if (gnc_csv_parse(parse_data, TRUE, &error))
01153         {
01154             /* If we couldn't parse the data ... */
01155             gnc_error_dialog(NULL, "%s", error->message);
01156         }
01157 
01158         /* Preview the data. */
01159         preview = gnc_csv_preview_new();
01160         if (gnc_csv_preview(preview, parse_data))
01161         {
01162             /* If the user clicked "Cancel", free everything and quit. */
01163             gnc_csv_preview_free(preview);
01164             gnc_csv_parse_data_free(parse_data);
01165             g_free(selected_filename);
01166             return;
01167         }
01168 
01169         /* Let the user select an account to put the transactions in. */
01170         account = gnc_import_select_account(NULL, NULL, 1, NULL, NULL, 0, NULL, NULL);
01171         if (account == NULL) /* Quit if the user canceled. */
01172         {
01173             gnc_csv_preview_free(preview);
01174             gnc_csv_parse_data_free(parse_data);
01175             g_free(selected_filename);
01176             return;
01177         }
01178 
01179         /* Create transactions from the parsed data. */
01180         gnc_csv_parse_to_trans(parse_data, account, FALSE);
01181 
01182         /* If there are errors, let the user try and eliminate them by
01183          * previewing them. Repeat until either there are no errors or the
01184          * user gives up. */
01185         while (!((parse_data->error_lines == NULL) || user_canceled))
01186         {
01187             user_canceled = gnc_csv_preview_errors(preview);
01188             gnc_csv_parse_to_trans(parse_data, account, TRUE);
01189         }
01190 
01191         /* Create the genereic transaction importer GUI. */
01192         gnc_csv_importer_gui = gnc_gen_trans_list_new(NULL, NULL, FALSE, 42);
01193 
01194         /* Get the list of the transactions that were created. */
01195         transactions = parse_data->transactions;
01196         /* Copy all of the transactions to the importer GUI. */
01197         while (transactions != NULL)
01198         {
01199             GncCsvTransLine* trans_line = transactions->data;
01200             gnc_gen_trans_list_add_trans(gnc_csv_importer_gui,
01201                                          trans_line->trans);
01202             transactions = g_list_next(transactions);
01203         }
01204         /* Let the user load those transactions into the account, so long
01205          * as there is at least one transaction to be loaded. */
01206         if (parse_data->transactions != NULL)
01207             gnc_gen_trans_list_run(gnc_csv_importer_gui);
01208         else
01209             gnc_gen_trans_list_delete(gnc_csv_importer_gui);
01210 
01211         /* Free the memory we allocated. */
01212         gnc_csv_preview_free(preview);
01213         gnc_csv_parse_data_free(parse_data);
01214         g_free(selected_filename);
01215     }
01216 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines