r16381 - gnucash/branches/csv-import/src/import-export/csv - Added support for error messages individualized for rows;

Benjamin Sperisen lasindi at cvs.gnucash.org
Fri Aug 3 07:23:58 EDT 2007


Author: lasindi
Date: 2007-08-03 07:23:56 -0400 (Fri, 03 Aug 2007)
New Revision: 16381
Trac: http://svn.gnucash.org/trac/changeset/16381

Modified:
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h
Log:
Added support for error messages individualized for rows;
started renaming "Amount" to "Balance";
got rid of some "uninitialized" warnings;
added some comments


Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c	2007-08-03 01:30:46 UTC (rev 16380)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c	2007-08-03 11:23:56 UTC (rev 16381)
@@ -213,14 +213,6 @@
   }
 }
 
-/* This array contains all of the different strings for different column types. */
-static char* column_type_strs[GNC_CSV_NUM_COL_TYPES] = {N_("None"),
-                                                        N_("Date"),
-                                                        N_("Description"),
-                                                        N_("Amount"),
-                                                        N_("Deposit"),
-                                                        N_("Withdrawal")};
-
 /** Event handler for selecting a new date format.
  * @param format_selector The combo box for selecting date formats
  * @param preview The data that is being configured
@@ -262,7 +254,7 @@
     for(type = 0; type < GNC_CSV_NUM_COL_TYPES; type++)
     {
       /* ... we find one that matches with what's in the column. */
-      if(!strcmp(contents, _(column_type_strs[type])))
+      if(!strcmp(contents, _(gnc_csv_column_type_strs[type])))
       {
         /* Set the column_types array appropriately and quit. */
         column_types->data[i] = type;
@@ -365,7 +357,7 @@
       {
         /* ... set this column to the "None" type. (We can't allow duplicate types.) */
         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 2*i+1,
-                           _(column_type_strs[GNC_CSV_NONE]), -1);
+                           _(gnc_csv_column_type_strs[GNC_CSV_NONE]), -1);
       }
     }
     else /* If this is the column that was edited ... */
@@ -779,6 +771,12 @@
     }
   }
 
+  /* Don't let the user affect the last column if it has error messages. */
+  if(preview->parse_data->orig_max_row < ncols && ncols - col == 1)
+  {
+    return;
+  }
+
   if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
   {
     make_new_column(preview, col, (int)event->x - offset, FALSE);
@@ -834,7 +832,7 @@
     for(j = 0; j < GNC_CSV_NUM_COL_TYPES; j++)
     {
       gtk_list_store_append(cstores[i], &iter);
-      gtk_list_store_set(cstores[i], &iter, 0, _(column_type_strs[j]), -1);
+      gtk_list_store_set(cstores[i], &iter, 0, _(gnc_csv_column_type_strs[j]), -1);
     }
   }
 
@@ -911,7 +909,7 @@
   for(i = 0; i < ncols; i++)
   {
     gtk_list_store_set(ctstore, &iter, 2*i, cstores[i], 2*i+1,
-                       _(column_type_strs[(int)(preview->parse_data->column_types->data[i])]),
+                       _(gnc_csv_column_type_strs[(int)(preview->parse_data->column_types->data[i])]),
                        -1);
   }
 
@@ -1016,6 +1014,8 @@
   GtkImage* instructions_image = GTK_IMAGE(glade_xml_get_widget(preview->xml, "instructions_image"));
   gchar* name;
   GtkIconSize size;
+  GtkTreeViewColumn* last_col;
+
   gtk_image_get_stock(instructions_image, &name, &size);
   gtk_image_set_from_stock(instructions_image, GTK_STOCK_DIALOG_ERROR, size);
   gtk_label_set_text(instructions_label,
@@ -1029,6 +1029,13 @@
 
   /* Wait until the user clicks "OK" or "Cancel". */
   gnc_csv_preview_update(preview, TRUE);
+
+  /* Set the last column to have the header "Errors" so that the user
+   * doesn't find the extra column confusing. */
+  last_col = gtk_tree_view_get_column(preview->treeview,
+                                      preview->parse_data->column_types->len - 1);
+  gtk_tree_view_column_set_title(last_col, _("Errors"));
+
   gtk_dialog_run(GTK_DIALOG(preview->dialog));
   
   if(preview->approved)

Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c	2007-08-03 01:30:46 UTC (rev 16380)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c	2007-08-03 11:23:56 UTC (rev 16381)
@@ -27,6 +27,14 @@
                                    N_("d-m"),
                                    N_("m-d")};
 
+/* This array contains all of the different strings for different column types. */
+gchar* gnc_csv_column_type_strs[GNC_CSV_NUM_COL_TYPES] = {N_("None"),
+                                                          N_("Date"),
+                                                          N_("Description"),
+                                                          N_("Balance"),
+                                                          N_("Deposit"),
+                                                          N_("Withdrawal")};
+
 /** A set of sensible defaults for parsing CSV files. 
  * @return StfParseOptions_t* for parsing a file with comma separators
  */
@@ -38,13 +46,19 @@
   return options;
 }
 
-/* TODO Comment */
+/** Parses a string into a date, given a format. The format must
+ * include the year. This function should only be called by
+ * parse_date.
+ * @param date_str The string containing a date being parsed
+ * @param format An index specifying a format in date_format_user
+ * @return The parsed value of date_str on success or -1 on failure
+ */
 static time_t parse_date_with_year(const char* date_str, int format)
 {
   time_t rawtime; /* The integer time */
   struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
   
-  int i, j, mem_length, orig_year, orig_month, orig_day;
+  int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1;
 
   /* Buffer for containing individual parts (e.g. year, month, day) of a date */
   char date_segment[5];
@@ -173,13 +187,19 @@
   }
 }
 
-/* TODO Comment */
+/** Parses a string into a date, given a format. The format cannot
+ * include the year. This function should only be called by
+ * parse_date.
+ * @param date_str The string containing a date being parsed
+ * @param format An index specifying a format in date_format_user
+ * @return The parsed value of date_str on success or -1 on failure
+ */
 static time_t parse_date_without_year(const char* date_str, int format)
 {
   time_t rawtime; /* The integer time */
   struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
   
-  int i, j, mem_length, orig_year, orig_month, orig_day;
+  int i, j, mem_length, orig_year = -1, orig_month = -1, orig_day = -1;
 
   /* Buffer for containing individual parts (e.g. year, month, day) of a date */
   gchar* date_segment;
@@ -294,6 +314,7 @@
   parse_data->raw_str.begin = parse_data->raw_str.end
     = parse_data->file_str.begin = parse_data->file_str.end = NULL;
   parse_data->orig_lines = NULL;
+  parse_data->orig_row_lengths = NULL;
   parse_data->column_types = NULL;
   parse_data->error_lines = parse_data->transactions = NULL;
   parse_data->options = default_parse_options();
@@ -316,8 +337,11 @@
     g_free(parse_data->file_str.begin);
 
   if(parse_data->orig_lines != NULL)
-    g_ptr_array_free(parse_data->orig_lines, TRUE);
+    stf_parse_general_free(parse_data->orig_lines);
 
+  if(parse_data->orig_row_lengths != NULL)
+    g_array_free(parse_data->orig_row_lengths, FALSE);
+
   if(parse_data->options != NULL)
     stf_parse_options_free(parse_data->options);
 
@@ -404,7 +428,7 @@
     /* TODO Handle file opening errors more specifically,
      * e.g. inexistent file versus no read permission. */
     parse_data->raw_str.begin = NULL;
-    g_set_error(error, 0, GNC_CSV_FILE_OPEN_ERR, "File opening failed.");
+    g_set_error(error, 0, GNC_CSV_FILE_OPEN_ERR, _("File opening failed."));
     return 1;
   }
 
@@ -418,7 +442,7 @@
                                 "UTF-8", NULL);
   if(guess_enc == NULL)
   {
-    g_set_error(error, 0, GNC_CSV_ENCODING_ERR, "Unknown encoding.");
+    g_set_error(error, 0, GNC_CSV_ENCODING_ERR, _("Unknown encoding."));
     return 1;
   }
 
@@ -427,7 +451,7 @@
   gnc_csv_convert_encoding(parse_data, guess_enc, error);
   if(parse_data->file_str.begin == NULL)
   {
-    g_set_error(error, 0, GNC_CSV_ENCODING_ERR, "Unknown encoding.");
+    g_set_error(error, 0, GNC_CSV_ENCODING_ERR, _("Unknown encoding."));
     return 1;
   }
   else
@@ -452,8 +476,12 @@
   /* max_cols is the number of columns in the row with the most columns. */
   int i, max_cols = 0;
 
+  if(parse_data->orig_lines != NULL)
+  {
+    stf_parse_general_free(parse_data->orig_lines);
+  }
+
   /* If everything is fine ... */
-  /* TODO Check about freeing parse_data->orig_lines ... */
   if(parse_data->file_str.begin != NULL)
   {
     /* Do the actual parsing. */
@@ -467,6 +495,21 @@
     parse_data->orig_lines = g_ptr_array_new();
   }
 
+  /* Record the original row lengths of parse_data->orig_lines. */
+  if(parse_data->orig_row_lengths != NULL)
+    g_array_free(parse_data->orig_row_lengths, FALSE);
+  parse_data->orig_row_lengths =
+    g_array_sized_new(FALSE, FALSE, sizeof(int), parse_data->orig_lines->len);
+  g_array_set_size(parse_data->orig_row_lengths, parse_data->orig_lines->len);
+  parse_data->orig_max_row = 0;
+  for(i = 0; i < parse_data->orig_lines->len; i++)
+  {
+    int length = ((GPtrArray*)parse_data->orig_lines->pdata[i])->len;
+    parse_data->orig_row_lengths->data[i] = length;
+    if(length > parse_data->orig_max_row)
+      parse_data->orig_max_row = length;
+  }
+
   /* If it failed, generate an error. */
   if(parse_data->orig_lines == NULL)
   {
@@ -527,46 +570,36 @@
 /** A struct encapsulating a property of a transaction. */
 typedef struct
 {
-  gboolean essential; /**< TRUE if every transaction needs this property */
   int type; /**< A value from the GncCsvColumnType enum except
              * GNC_CSV_NONE and GNC_CSV_NUM_COL_TYPES */
   void* value; /**< Pointer to the data that will be used to configure a transaction */
-  TransPropertyList* set; /**< The set the property belongs to */
+  TransPropertyList* list; /**< The list the property belongs to */
 } TransProperty;
 
-/** Constructor TransProperty.
+/** Constructor for TransProperty.
  * @param type The type of the new property (see TransProperty.type for possible values)
  */
-static TransProperty* trans_property_new(int type, TransPropertyList* set)
+static TransProperty* trans_property_new(int type, TransPropertyList* list)
 {
   TransProperty* prop = g_new(TransProperty, 1);
   prop->type = type;
-  prop->set = set;
+  prop->list = list;
   prop->value = NULL;
-  
-  switch(type)
-  {
-    /* Only the "Date" and "Amount" properties are essential. */
-  case GNC_CSV_DATE:
-  case GNC_CSV_AMOUNT:
-    prop->essential = TRUE;
-    break;
-
-  default:
-    prop->essential = FALSE;
-  }
   return prop;
 }
 
+/** Destructor for TransProperty.
+ * @param prop The property to be freed
+ */
 static void trans_property_free(TransProperty* prop)
 {
   switch(prop->type)
   {
-    /* The types for "Date" and "Amount" (time_t and gnc_numeric,
+    /* The types for "Date" and "Balance" (time_t and gnc_numeric,
      * respectively) are typically not pointed to, we have to free
      * them, unlike types like char* ("Description"). */
   case GNC_CSV_DATE:
-  case GNC_CSV_AMOUNT:
+  case GNC_CSV_BALANCE:
   case GNC_CSV_DEPOSIT:
   case GNC_CSV_WITHDRAWAL:
     if(prop->value != NULL)
@@ -591,14 +624,14 @@
   {
   case GNC_CSV_DATE:
     prop->value = g_new(time_t, 1);
-    *((time_t*)(prop->value)) = parse_date(str, prop->set->date_format);
+    *((time_t*)(prop->value)) = parse_date(str, prop->list->date_format);
     return *((time_t*)(prop->value)) != -1;
 
   case GNC_CSV_DESCRIPTION:
     prop->value = g_strdup(str);
     return TRUE;
 
-  case GNC_CSV_AMOUNT:
+  case GNC_CSV_BALANCE:
   case GNC_CSV_DEPOSIT:
   case GNC_CSV_WITHDRAWAL:
     str_dupe = g_strdup(str); /* First, we make a copy so we can't mess up real data. */
@@ -640,7 +673,8 @@
     if(abs(value) > 0.00001)
     {
       prop->value = g_new(gnc_numeric, 1);
-      *((gnc_numeric*)(prop->value)) = double_to_gnc_numeric(value, xaccAccountGetCommoditySCU(prop->set->account),
+      *((gnc_numeric*)(prop->value)) = double_to_gnc_numeric(value,
+                                                             xaccAccountGetCommoditySCU(prop->list->account),
                                                              GNC_RND_ROUND);
     }
     return TRUE;
@@ -648,36 +682,54 @@
   return FALSE; /* We should never actually get here. */
 }
 
-/* TODO Comment */
+/** Constructor for TransPropertyList.
+ * @param account The account with which transactions should be built
+ * @param date_format An index from date_format_user for how date properties should be parsed
+ * @return A pointer to a new TransPropertyList
+ */
 static TransPropertyList* trans_property_list_new(Account* account, int date_format)
 {
-  TransPropertyList* set = g_new(TransPropertyList, 1);
-  set->account = account;
-  set->date_format = date_format;
-  set->properties = NULL;
-  return set;
+  TransPropertyList* list = g_new(TransPropertyList, 1);
+  list->account = account;
+  list->date_format = date_format;
+  list->properties = NULL;
+  return list;
 }
 
-/* TODO Comment */
-static void trans_property_list_free(TransPropertyList* set)
+/** Destructor for TransPropertyList.
+ * @param list The list to be freed
+ */
+static void trans_property_list_free(TransPropertyList* list)
 {
-  GList* properties_begin = set->properties;
-  while(set->properties != NULL)
+  /* Free all of the properties in this list before freeeing the list itself. */
+  GList* properties_begin = list->properties;
+  while(list->properties != NULL)
   {
-    trans_property_free((TransProperty*)(set->properties->data));
-    set->properties = g_list_next(set->properties);
+    trans_property_free((TransProperty*)(list->properties->data));
+    list->properties = g_list_next(list->properties);
   }
   g_list_free(properties_begin);
-  g_free(set);
+  g_free(list);
 }
 
-/* TODO Comment */
+/** Adds a property to the list it's linked with.
+ * (The TransPropertyList is not passed as a parameter because the property is
+ * associated with a list when it's constructed.)
+ * @param property The property to be added to its list
+ */
 static void trans_property_list_add(TransProperty* property)
 {
-  property->set->properties = g_list_append(property->set->properties, property);
+  property->list->properties = g_list_append(property->list->properties, property);
 }
 
-/* TODO Comment */
+/** Adds a split to a transaction.
+ * This function is only called by trans_property_list_to_trans. This is in a
+ * separate function to avoid copying code twice in trans_property_list_to_trans.
+ * @param trans The transaction to add a split to
+ * @param account The account used for the split
+ * @param book The book where the split should be stored
+ * @param amount The amount of the split
+ */
 static void trans_add_split(Transaction* trans, Account* account, GNCBook* book,
                             gnc_numeric amount)
 {
@@ -689,32 +741,112 @@
   xaccSplitSetAction(split, "Deposit");
 }
 
+/** Tests a TransPropertyList for having enough essential properties.
+ * Essential properties are "Date" and one of the following: "Balance", "Deposit", or
+ * "Withdrawal".
+ * @param list The list we are checking
+ * @param error Contains an error message on failure
+ * @return TRUE if there are enough essentials; FALSE otherwise
+ */
+static gboolean trans_property_list_verify_essentials(TransPropertyList* list, gchar** error)
+{
+  int i;
+  /* possible_errors lists the ways in which a list can fail this test. */
+  enum PossibleErrorTypes {NO_DATE, NO_AMOUNT, NUM_OF_POSSIBLE_ERRORS};
+  gchar* possible_errors[NUM_OF_POSSIBLE_ERRORS] = {N_("No date column."),
+                                                    N_("No balance, deposit, or withdrawal column.")};
+  int possible_error_lengths[NUM_OF_POSSIBLE_ERRORS] = {0};
+  GList *properties_begin = list->properties, *errors_list = NULL;
+
+  /* Go through each of the properties and erase possible errors. */
+  while(list->properties)
+  {
+    switch(((TransProperty*)(list->properties->data))->type)
+    {
+    case GNC_CSV_DATE:
+      possible_errors[NO_DATE] = NULL;
+      break;
+
+    case GNC_CSV_BALANCE:
+    case GNC_CSV_DEPOSIT:
+    case GNC_CSV_WITHDRAWAL:
+      possible_errors[NO_AMOUNT] = NULL;
+      break;
+    }
+    list->properties = g_list_next(list->properties);
+  }
+  list->properties = properties_begin;
+
+  /* Accumulate a list of the actual errors. */
+  for(i = 0; i < NUM_OF_POSSIBLE_ERRORS; i++)
+  {
+    if(possible_errors[i] != NULL)
+    {
+      errors_list = g_list_append(errors_list, GINT_TO_POINTER(i));
+      /* Since we added an error, we want to also store its length for
+       * when we construct the full error string. */
+      possible_error_lengths[i] = strlen(_(possible_errors[i]));
+    }
+  }
+
+  /* If there are no errors, we can quit now. */
+  if(errors_list == NULL)
+    return TRUE;
+  else
+  {
+    /* full_error_size is the full length of the error message. */
+    int full_error_size = 0, string_length = 0;
+    GList* errors_list_begin = errors_list;
+    gchar *error_message, *error_message_begin;
+
+    /* Find the value for full_error_size. */
+    while(errors_list)
+    {
+      /* We add an extra 1 to account for spaces in between messages. */
+      full_error_size += possible_error_lengths[GPOINTER_TO_INT(errors_list->data)] + 1;
+      errors_list = g_list_next(errors_list);
+    }
+    errors_list = errors_list_begin;
+
+    /* Append the error messages one after another. */
+    error_message = error_message_begin = g_new(gchar, full_error_size);
+    while(errors_list)
+    {
+      i = GPOINTER_TO_INT(errors_list->data);
+      string_length = possible_error_lengths[i];
+      
+      /* Copy the error message and put a space after it. */
+      strncpy(error_message, _(possible_errors[i]), string_length);
+      error_message += string_length;
+      *error_message = ' ';
+      error_message++;
+      
+      errors_list = g_list_next(errors_list);
+    }
+    *error_message = '\0'; /* Replace the last space with the null byte. */
+    g_list_free(errors_list_begin);
+
+    *error = error_message_begin;
+    return FALSE;
+  }
+}
+
+/** Create a Transaction from a TransPropertyList.
+ * @param list The list of properties
+ * @param error Contains an error on failure
+ * @return On success, a pointer to a new transaction; on failure, NULL
+ */
 /* TODO Comment */
-static Transaction* trans_property_list_to_trans(TransPropertyList* set)
+static Transaction* trans_property_list_to_trans(TransPropertyList* list, gchar** error)
 {
   Transaction* trans;
-  GList* properties_begin = set->properties;
-  GNCBook* book = gnc_account_get_book(set->account);
-  gnc_commodity* currency = xaccAccountGetCommodity(set->account);
+  GList* properties_begin = list->properties;
+  GNCBook* book = gnc_account_get_book(list->account);
+  gnc_commodity* currency = xaccAccountGetCommodity(list->account);
   gnc_numeric amount;
-  gboolean alreadyHaveDepositOrWithdrawal = FALSE, noAmountYet = TRUE;
+  gboolean noAmountYet = TRUE;
   
-  unsigned int essential_properties_left = 2;
-  while(set->properties != NULL)
-  {
-    if(((TransProperty*)(set->properties->data))->essential)
-      essential_properties_left--;
-    else if(((TransProperty*)(set->properties->data))->type == GNC_CSV_DEPOSIT ||
-            ((TransProperty*)(set->properties->data))->type == GNC_CSV_WITHDRAWAL)
-    {
-      if(alreadyHaveDepositOrWithdrawal)
-        essential_properties_left--;
-      else
-        alreadyHaveDepositOrWithdrawal = TRUE;
-    }
-    set->properties = g_list_next(set->properties);
-  }
-  if(essential_properties_left)
+  if(!trans_property_list_verify_essentials(list, error))
     return NULL;
 
   trans = xaccMallocTransaction(book);
@@ -722,10 +854,10 @@
   xaccTransBeginEdit(trans);
   xaccTransSetCurrency(trans, currency);
 
-  set->properties = properties_begin;
-  while(set->properties != NULL)
+  list->properties = properties_begin;
+  while(list->properties != NULL)
   {
-    TransProperty* prop = (TransProperty*)(set->properties->data);
+    TransProperty* prop = (TransProperty*)(list->properties->data);
     switch(prop->type)
     {
     case GNC_CSV_DATE:
@@ -738,7 +870,7 @@
 
     case GNC_CSV_DEPOSIT:
     case GNC_CSV_WITHDRAWAL:
-    case GNC_CSV_AMOUNT:
+    case GNC_CSV_BALANCE:
       if(noAmountYet && prop->value != NULL)
       {
         /* Withdrawals are just negative deposits. */
@@ -747,18 +879,18 @@
         else
           amount = *((gnc_numeric*)(prop->value));
 
-        trans_add_split(trans, set->account, book, amount);
+        trans_add_split(trans, list->account, book, amount);
         
         noAmountYet = FALSE;
       }
     }
-    set->properties = g_list_next(set->properties);
+    list->properties = g_list_next(list->properties);
   }
 
   if(noAmountYet)
   {
-    amount = double_to_gnc_numeric(0.0, xaccAccountGetCommoditySCU(set->account), GNC_RND_ROUND);
-    trans_add_split(trans, set->account, book, amount);
+    amount = double_to_gnc_numeric(0.0, xaccAccountGetCommoditySCU(list->account), GNC_RND_ROUND);
+    trans_add_split(trans, list->account, book, amount);
   }
 
   return trans;
@@ -777,7 +909,7 @@
 int gnc_parse_to_trans(GncCsvParseData* parse_data, Account* account,
                        gboolean redo_errors)
 {
-  int i, j;
+  int i, j, max_cols = 0;
   GArray* column_types = parse_data->column_types;
   GList *error_lines = NULL, *begin_error_lines = NULL;
   GList* last_transaction = NULL;
@@ -814,7 +946,8 @@
     GPtrArray* line = parse_data->orig_lines->pdata[i];
     /* This flag is TRUE if there are any errors in this row. */
     gboolean errors = FALSE;
-    TransPropertyList* set = trans_property_list_new(account, parse_data->date_format);
+    gchar* error_message = NULL;
+    TransPropertyList* list = trans_property_list_new(account, parse_data->date_format);
     Transaction* trans = NULL;
 
     /* TODO There should eventually be a specification of what errors
@@ -826,7 +959,7 @@
       if(column_types->data[j] != GNC_CSV_NONE)
       {
         /* Affect the transaction appropriately. */
-        TransProperty* property = trans_property_new(column_types->data[j], set);
+        TransProperty* property = trans_property_new(column_types->data[j], list);
         gboolean succeeded = trans_property_set(property, line->pdata[j]);
         /* TODO Maybe move error handling to within TransPropertyList functions? */
         if(succeeded)
@@ -836,6 +969,8 @@
         else
         {
           errors = TRUE;
+          error_message = g_strdup_printf(_("%s column could not be understood."),
+                                          _(gnc_csv_column_type_strs[property->type]));
           trans_property_free(property);
           break;
         }
@@ -845,21 +980,32 @@
     /* If we had success, add the transaction to parse_data->transaction. */
     if(!errors)
     {
-      trans = trans_property_list_to_trans(set);
+      trans = trans_property_list_to_trans(list, &error_message);
       errors = trans == NULL;
     }
 
-    trans_property_list_free(set);
-      
+    trans_property_list_free(list);
 
-    /* If there was no success, add this line to parse_data->error_lines. */
+    /* If there were errors, add this line to parse_data->error_lines. */
     if(errors)
     {
       parse_data->error_lines = g_list_append(parse_data->error_lines,
                                               GINT_TO_POINTER(i));
+      /* If there's already an error message, we need to replace it. */
+      if(line->len > (int)(parse_data->orig_row_lengths->data[i]))
+      {
+        g_free(line->pdata[line->len - 1]);
+        line->pdata[line->len - 1] = error_message;
+      }
+      else
+      {
+        /* Put the error message at the end of the line. */
+        g_ptr_array_add(line, error_message);
+      }
     }
     else
     {
+      /* If all went well, add this transaction to the list. */
       GncCsvTransLine* trans_line = g_new(GncCsvTransLine, 1);
 
       trans_line->trans = trans;
@@ -867,6 +1013,8 @@
 
       if(redo_errors)
       {
+        /* If we are correcting errors, find the right place to insert
+         * the transaction. */
         while(last_transaction != NULL &&
               ((GncCsvTransLine*)(last_transaction->data))->line_no < i)
         {
@@ -900,5 +1048,19 @@
   {
     g_list_free(begin_error_lines);
   }
+
+  /* We need to resize parse_data->column_types since errors may have added columns. */
+  for(i = 0; i < parse_data->orig_lines->len; i++)
+  {
+    if(max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
+      max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
+  }
+  i = parse_data->column_types->len;
+  parse_data->column_types = g_array_set_size(parse_data->column_types, max_cols);
+  for(; i < max_cols; i++)
+  {
+    parse_data->column_types->data[i] = GNC_CSV_NONE;
+  }
+
   return 0;
 }

Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h	2007-08-03 01:30:46 UTC (rev 16380)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h	2007-08-03 11:23:56 UTC (rev 16381)
@@ -41,7 +41,7 @@
 enum GncCsvColumnType {GNC_CSV_NONE,
                        GNC_CSV_DATE,
                        GNC_CSV_DESCRIPTION,
-                       GNC_CSV_AMOUNT,
+                       GNC_CSV_BALANCE,
                        GNC_CSV_DEPOSIT,
                        GNC_CSV_WITHDRAWAL,
                        GNC_CSV_NUM_COL_TYPES};
@@ -78,6 +78,9 @@
 /* A set of date formats that the user sees. */
 extern const gchar* date_format_user[];
 
+/* This array contains all of the different strings for different column types. */
+extern gchar* gnc_csv_column_type_strs[];
+
 /** Struct containing data for parsing a CSV/Fixed-Width file. */
 typedef struct
 {
@@ -86,6 +89,9 @@
   GncCsvStr raw_str; /**< Untouched data from the file as a string */
   GncCsvStr file_str; /**< raw_str translated into UTF-8 */
   GPtrArray* orig_lines; /**< file_str parsed into a two-dimensional array of strings */
+  GArray* orig_row_lengths; /**< The lengths of rows in orig_lines
+                             * before error messages are appended */
+  int orig_max_row; /**< Holds the maximum value in orig_row_lengths */
   GStringChunk* chunk; /**< A chunk of memory in which the contents of orig_lines is stored */
   StfParseOptions_t* options; /**< Options controlling how file_str should be parsed */
   GArray* column_types; /**< Array of values from the GncCsvColumnType enumeration */



More information about the gnucash-changes mailing list