|
GnuCash 2.3.0
|
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 }
1.7.4