r16356 - gnucash/branches/csv-import/src/import-export/csv - Added support for fixed-width files,

Benjamin Sperisen lasindi at cvs.gnucash.org
Wed Jul 25 02:53:59 EDT 2007


Author: lasindi
Date: 2007-07-25 02:53:58 -0400 (Wed, 25 Jul 2007)
New Revision: 16356
Trac: http://svn.gnucash.org/trac/changeset/16356

Added:
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.c
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.h
Modified:
   gnucash/branches/csv-import/src/import-export/csv/Makefile.am
   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
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade
Log:
Added support for fixed-width files,
added support for 'm-d' and 'd-m' dates (i.e. dates without years),
and minor polishing


Modified: gnucash/branches/csv-import/src/import-export/csv/Makefile.am
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/Makefile.am	2007-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/Makefile.am	2007-07-25 06:53:58 UTC (rev 16356)
@@ -6,12 +6,14 @@
   gncmod-csv-import.c \
   gnc-plugin-csv.c \
   gnc-csv-import.c \
-  gnc-csv-model.c
+  gnc-csv-model.c \
+  gnc-csv-gnumeric-popup.c
 
 noinst_HEADERS = \
   gnc-plugin-csv.h \
   gnc-csv-import.h \
-  gnc-csv-model.h
+  gnc-csv-model.h \
+  gnc-csv-gnumeric-popup.h
 
 libgncmod_csv_la_LDFLAGS = -avoid-version $(pkg-config --libs libgoffice-0.3)
 

Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.c	2007-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.c	2007-07-25 06:53:58 UTC (rev 16356)
@@ -0,0 +1,150 @@
+/* The following is code copied from Gnumeric 1.7.8 licensed under the
+ * GNU General Public License version 2. It is from the file
+ * gnumeric/src/gui-util.c, and it has been modified slightly to work
+ * within GnuCash. */
+
+#include "gnc-csv-gnumeric-popup.h"
+
+#include <glib/gi18n.h>
+
+static void
+popup_item_activate (GtkWidget *item, gpointer *user_data)
+{
+	GnumericPopupMenuElement const *elem =
+		g_object_get_data (G_OBJECT (item), "descriptor");
+	GnumericPopupMenuHandler handler =
+		g_object_get_data (G_OBJECT (item), "handler");
+
+	g_return_if_fail (elem != NULL);
+	g_return_if_fail (handler != NULL);
+
+	if (handler (elem, user_data))
+		gtk_widget_destroy (gtk_widget_get_toplevel (item));
+}
+
+static void
+gnumeric_create_popup_menu_list (GSList *elements,
+				 GnumericPopupMenuHandler handler,
+				 gpointer user_data,
+				 int display_filter,
+				 int sensitive_filter,
+				 GdkEventButton *event)
+{
+	GtkWidget *menu, *item;
+	char const *trans;
+
+	menu = gtk_menu_new ();
+
+	for (; elements != NULL ; elements = elements->next) {
+		GnumericPopupMenuElement const *element = elements->data;
+		char const * const name = element->name;
+		char const * const pix_name = element->pixmap;
+
+		item = NULL;
+
+		if (element->display_filter != 0 &&
+		    !(element->display_filter & display_filter))
+			continue;
+
+		if (name != NULL && *name != '\0') {
+			trans = _(name);
+			item = gtk_image_menu_item_new_with_mnemonic (trans);
+			if (element->sensitive_filter != 0 &&
+			    (element->sensitive_filter & sensitive_filter))
+				gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
+			if (pix_name != NULL) {
+				GtkWidget *image = gtk_image_new_from_stock (pix_name,
+                                        GTK_ICON_SIZE_MENU);
+				gtk_widget_show (image);
+				gtk_image_menu_item_set_image (
+					GTK_IMAGE_MENU_ITEM (item),
+					image);
+			}
+		} else {
+			/* separator */
+			item = gtk_menu_item_new ();
+			gtk_widget_set_sensitive (item, FALSE);
+		}
+
+		if (element->index != 0) {
+			g_signal_connect (G_OBJECT (item),
+				"activate",
+				G_CALLBACK (&popup_item_activate), user_data);
+			g_object_set_data (
+				G_OBJECT (item), "descriptor", (gpointer)(element));
+			g_object_set_data (
+				G_OBJECT (item), "handler", (gpointer)handler);
+		}
+
+		gtk_widget_show (item);
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+	}
+
+	gnumeric_popup_menu (GTK_MENU (menu), event);
+}
+
+void
+gnumeric_create_popup_menu (GnumericPopupMenuElement const *elements,
+			    GnumericPopupMenuHandler handler,
+			    gpointer user_data,
+			    int display_filter, int sensitive_filter,
+			    GdkEventButton *event)
+{
+	int i;
+	GSList *tmp = NULL;
+
+	for (i = 0; elements [i].name != NULL; i++)
+		tmp = g_slist_prepend (tmp, (gpointer)(elements + i));
+
+	tmp = g_slist_reverse (tmp);
+	gnumeric_create_popup_menu_list (tmp, handler, user_data,
+		display_filter, sensitive_filter, event);
+	g_slist_free (tmp);
+}
+
+static void
+kill_popup_menu (GtkWidget *widget, GtkMenu *menu)
+{
+	g_return_if_fail (menu != NULL);
+	g_return_if_fail (GTK_IS_MENU (menu));
+
+	g_object_unref (G_OBJECT (menu));
+}
+
+/**
+ * gnumeric_popup_menu :
+ * @menu : #GtkMenu
+ * @event : #GdkEventButton optionally NULL
+ *
+ * Bring up a popup and if @event is non-NULL ensure that the popup is on the
+ * right screen.
+ **/
+void
+gnumeric_popup_menu (GtkMenu *menu, GdkEventButton *event)
+{
+	g_return_if_fail (menu != NULL);
+	g_return_if_fail (GTK_IS_MENU (menu));
+
+#if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
+	g_object_ref_sink (menu);
+#else
+	g_object_ref (menu);
+	gtk_object_sink (GTK_OBJECT (menu));
+#endif
+
+	if (event)
+		gtk_menu_set_screen (menu,
+				     gdk_drawable_get_screen (event->window));
+
+	g_signal_connect (G_OBJECT (menu),
+		"hide",
+		G_CALLBACK (kill_popup_menu), menu);
+
+	/* Do NOT pass the button used to create the menu.
+	 * instead pass 0.  Otherwise bringing up a menu with
+	 * the right button will disable clicking on the menu with the left.
+	 */
+	gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, 
+			(event != NULL) ? event->time 
+			: gtk_get_current_event_time());
+}

Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.h	2007-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-gnumeric-popup.h	2007-07-25 06:53:58 UTC (rev 16356)
@@ -0,0 +1,34 @@
+/* The following is code copied from Gnumeric 1.7.8 licensed under the
+ * GNU General Public License version 2. It is from the file
+ * gnumeric/src/gui-util.h, and it has been modified slightly to work
+ * within GnuCash. */
+
+#ifndef GNC_CSV_GNUMERIC_POPUP
+#define GNC_CSV_GNUMERIC_POPUP
+
+#include <gtk/gtk.h>
+
+typedef struct {
+	char const *name;
+	char const *pixmap;
+	int display_filter;
+	int sensitive_filter;
+
+	int index;
+} GnumericPopupMenuElement;
+
+typedef gboolean (*GnumericPopupMenuHandler) (GnumericPopupMenuElement const *e,
+					      gpointer user_data);
+
+/* Use this on menus that are popped up */
+void gnumeric_popup_menu (GtkMenu *menu, GdkEventButton *event);
+
+void gnumeric_create_popup_menu (GnumericPopupMenuElement const *elements,
+				 GnumericPopupMenuHandler handler,
+				 gpointer user_data,
+				 int display_filter,
+				 int sensitive_filter,
+				 GdkEventButton *event);
+
+
+#endif

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-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c	2007-07-25 06:53:58 UTC (rev 16356)
@@ -39,6 +39,7 @@
 
 #include "gnc-csv-import.h"
 #include "gnc-csv-model.h"
+#include "gnc-csv-gnumeric-popup.h"
 
 #define GCONF_SECTION "dialogs/import/csv"
 
@@ -76,9 +77,14 @@
                             * 2. encoding_selected is called twice,
                             * each time decrementing this by 1. */
   gboolean approved; /**< This is FALSE until the user clicks "OK". */
+  GtkWidget** treeview_buttons; /**< This array contains the header buttons in treeview */
+  int longest_line; /**< The length of the longest row */
+  int fixed_context_col; /**< The number of the column whose the user has clicked */
+  int fixed_context_dx; /**< The horizontal coordinate of the pixel in the header of the column
+                         * the user has clicked */
 } GncCsvPreview;
 
-static void gnc_csv_preview_treeview(GncCsvPreview* preview, gboolean notEmpty);
+static void gnc_csv_preview_update(GncCsvPreview* preview, gboolean notEmpty);
 
 /** Event handler for separator changes. This function is called
  * whenever one of the widgets for configuring the separators (the
@@ -138,9 +144,29 @@
   }
 
   /* If we parsed successfully, redisplay the data. */
-  gnc_csv_preview_treeview(preview, TRUE);
+  gnc_csv_preview_update(preview, TRUE);
 }
 
+/* TODO Comment */
+static void separated_or_fixed_selected(GtkToggleButton* csv_button, GncCsvPreview* preview)
+{
+  GError* error = NULL;
+  if(gtk_toggle_button_get_active(csv_button)) /* If we're in CSV mode ... */
+  {
+    stf_parse_options_set_type(preview->parse_data->options, PARSE_TYPE_CSV);
+  }
+  else /* If we're in fixed-width mode ... */
+  {
+    stf_parse_options_set_type(preview->parse_data->options, PARSE_TYPE_FIXED);
+  }
+  if(gnc_csv_parse(preview->parse_data, FALSE, &error))
+  {
+    gnc_error_dialog(NULL, "%s", error->message);
+    return;
+  }
+  gnc_csv_preview_update(preview, TRUE);
+}
+
 /** Event handler for a new encoding. This is called when the user
  * selects a new encoding; the data is reparsed and shown to the
  * user.
@@ -178,7 +204,7 @@
       return;
     }
 
-    gnc_csv_preview_treeview(preview, TRUE);
+    gnc_csv_preview_update(preview, TRUE);
     preview->encoding_selected_called = FALSE;
   }
   else /* If this is the first call of the function ... */
@@ -357,7 +383,7 @@
 {
   int i;
   GncCsvPreview* preview = g_new(GncCsvPreview, 1);
-  GtkWidget *ok_button, *cancel_button;
+  GtkWidget *ok_button, *cancel_button, *csv_button;
   GtkContainer* date_format_container;
   /* The names in the glade file for the sep buttons. */
   char* sep_button_names[] = {"space_cbutton",
@@ -368,6 +394,7 @@
                               "hyphen_cbutton"};
   /* The table containing preview->encselector and the separator configuration widgets */
   GtkTable* enctable;
+  PangoContext* context; /* Used to set a monotype font on preview->treeview */
 
   preview->encselector = GO_CHARMAP_SEL(go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8));
   /* Connect the selector to the encoding_selected event handler. */
@@ -435,10 +462,16 @@
   g_signal_connect(G_OBJECT(cancel_button), "clicked",
                    G_CALLBACK(cancel_button_clicked), (gpointer)preview);
 
+  /* Connect the CSV/Fixed-Width radio button event handler. */
+  csv_button = glade_xml_get_widget(preview->xml, "csv_button");
+  g_signal_connect(csv_button, "toggled",
+                   G_CALLBACK(separated_or_fixed_selected), (gpointer)preview);                   
+
   /* Load the data treeview and connect it to its resizing event handler. */
   preview->treeview = (GtkTreeView*)(glade_xml_get_widget(preview->xml, "treeview"));
   g_signal_connect(G_OBJECT(preview->treeview), "size-allocate",
                    G_CALLBACK(treeview_resized), (gpointer)preview);
+  context = gtk_widget_create_pango_context(GTK_WIDGET(preview->treeview));
 
   /* Load the column type treeview. */
   preview->ctreeview = (GtkTreeView*)(glade_xml_get_widget(preview->xml, "ctreeview"));
@@ -460,12 +493,308 @@
   g_free(preview);
 }
 
-/* This function loads the preview's data (preview->parse_data) into
- * its data treeview. notEmpty is TRUE when the data treeview already
- * contains data, FALSE otherwise (e.g. the first time this function
- * is called on a preview). */
+/** Returns the cell renderer from a column in the preview's treeview.
+ * @param preview The data that is being configured
+ * @param col The number of the column whose cell renderer is being retrieved
+ * @return The cell renderer of column number col
+ */
+static GtkCellRenderer* gnc_csv_preview_get_cell_renderer(GncCsvPreview* preview, int col)
+{
+  GList* renderers = gtk_tree_view_column_get_cell_renderers(gtk_tree_view_get_column(preview->treeview, col));
+  GtkCellRenderer* cell = GTK_CELL_RENDERER(renderers->data);
+  g_list_free(renderers);
+  return cell;
+}
+
+/* The following is code copied from Gnumeric 1.7.8 licensed under the
+ * GNU General Public License version 2. It is from the file
+ * gnumeric/src/dialogs/dialog-stf-fixed-page.c, and it has been
+ * modified slightly to work within GnuCash. */
+
+/* ---- Beginning of Gnumeric Code ---- */
+
+/*
+ * Copyright 2001 Almer S. Tigelaar <almer at gnome.org>
+ * Copyright 2003 Morten Welinder <terra at gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+enum {
+	CONTEXT_STF_IMPORT_MERGE_LEFT = 1,
+	CONTEXT_STF_IMPORT_MERGE_RIGHT = 2,
+	CONTEXT_STF_IMPORT_SPLIT = 3,
+	CONTEXT_STF_IMPORT_WIDEN = 4,
+	CONTEXT_STF_IMPORT_NARROW = 5
+};
+
+static GnumericPopupMenuElement const popup_elements[] = {
+	{ N_("Merge with column on _left"), GTK_STOCK_REMOVE,
+	  0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT },
+	{ N_("Merge with column on _right"), GTK_STOCK_REMOVE,
+	  0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT },
+	{ "", NULL, 0, 0, 0 },
+	{ N_("_Split this column"), NULL,
+	  0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT },
+	{ "", NULL, 0, 0, 0 },
+	{ N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
+	  0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN },
+	{ N_("_Narrow this column"), GTK_STOCK_GO_BACK,
+	  0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW },
+	{ NULL, NULL, 0, 0, 0 },
+};
+
+static gboolean
+make_new_column (GncCsvPreview *preview, int col, int dx, gboolean test_only)
+{
+	PangoLayout *layout;
+	PangoFontDescription *font_desc;
+	int charindex, width;
+	GtkCellRenderer *cell =	gnc_csv_preview_get_cell_renderer(preview, col);
+	int colstart, colend;
+        GError* error = NULL;
+
+	colstart = (col == 0)
+		? 0
+		: stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col - 1);
+	colend = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
+
+	g_object_get (G_OBJECT (cell), "font_desc", &font_desc, NULL);
+	layout = gtk_widget_create_pango_layout (GTK_WIDGET (preview->treeview), "x");
+	pango_layout_set_font_description (layout, font_desc);
+	pango_layout_get_pixel_size (layout, &width, NULL);
+	if (width < 1) width = 1;
+	charindex = colstart + (dx + width / 2) / width;
+	g_object_unref (layout);
+	pango_font_description_free (font_desc);
+
+	if (charindex <= colstart || (colend != -1 && charindex >= colend))
+		return FALSE;
+
+	if (!test_only) {
+		stf_parse_options_fixed_splitpositions_add (preview->parse_data->options, charindex);
+                if(gnc_csv_parse(preview->parse_data, FALSE, &error))
+                {
+                  gnc_error_dialog(NULL, "%s", error->message);
+                  return FALSE;
+                }
+		gnc_csv_preview_update (preview, TRUE);
+	}
+
+	return TRUE;
+}
+
+
+static gboolean
+widen_column (GncCsvPreview *preview, int col, gboolean test_only)
+{
+	int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
+	int nextstart, nextnextstart;
+        GError* error = NULL;
+
+	if (col >= colcount - 1)
+		return FALSE;
+
+	nextstart = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
+
+	nextnextstart = (col == colcount - 2)
+		? preview->longest_line
+		: stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col + 1);
+
+	if (nextstart + 1 >= nextnextstart)
+		return FALSE;
+
+	if (!test_only) {
+		stf_parse_options_fixed_splitpositions_remove (preview->parse_data->options, nextstart);
+		stf_parse_options_fixed_splitpositions_add (preview->parse_data->options, nextstart + 1);
+                if(gnc_csv_parse(preview->parse_data, FALSE, &error))
+                {
+                  gnc_error_dialog(NULL, "%s", error->message);
+                  return FALSE;
+                }
+		gnc_csv_preview_update (preview, TRUE);
+	}
+	return TRUE;
+}
+
+static gboolean
+narrow_column (GncCsvPreview *preview, int col, gboolean test_only)
+{
+	int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
+	int thisstart, nextstart;
+        GError* error = NULL;
+
+	if (col >= colcount - 1)
+		return FALSE;
+
+	thisstart = (col == 0)
+		? 0
+		: stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col - 1);
+	nextstart = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
+
+	if (nextstart - 1 <= thisstart)
+		return FALSE;
+
+	if (!test_only) {
+		stf_parse_options_fixed_splitpositions_remove (preview->parse_data->options, nextstart);
+		stf_parse_options_fixed_splitpositions_add (preview->parse_data->options, nextstart - 1);
+                if(gnc_csv_parse(preview->parse_data, FALSE, &error))
+                {
+                  gnc_error_dialog(NULL, "%s", error->message);
+                  return FALSE;
+                }
+		gnc_csv_preview_update (preview, TRUE);
+	}
+	return TRUE;
+}
+
+static gboolean
+delete_column (GncCsvPreview *preview, int col, gboolean test_only)
+{
+        GError* error = NULL;
+	int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
+	if (col < 0 || col >= colcount - 1)
+		return FALSE;
+
+	if (!test_only) {
+		int nextstart = stf_parse_options_fixed_splitpositions_nth (preview->parse_data->options, col);
+		stf_parse_options_fixed_splitpositions_remove (preview->parse_data->options, nextstart);
+                if(gnc_csv_parse(preview->parse_data, FALSE, &error))
+                {
+                  gnc_error_dialog(NULL, "%s", error->message);
+                  return FALSE;
+                }
+		gnc_csv_preview_update (preview, TRUE);
+	}
+	return TRUE;
+}
+
+static void
+select_column (GncCsvPreview *preview, int col)
+{
+        GError* error = NULL;
+	int colcount = stf_parse_options_fixed_splitpositions_count (preview->parse_data->options);
+	GtkTreeViewColumn *column;
+
+	if (col < 0 || col >= colcount)
+		return;
+
+	column = gtk_tree_view_get_column (preview->treeview, col);
+	gtk_widget_grab_focus (column->button);
+}
+
+static gboolean
+fixed_context_menu_handler (GnumericPopupMenuElement const *element,
+			    gpointer user_data)
+{
+	GncCsvPreview *preview = user_data;
+	int col = preview->fixed_context_col;
+
+	switch (element->index) {
+	case CONTEXT_STF_IMPORT_MERGE_LEFT:
+		delete_column (preview, col - 1, FALSE);
+		break;
+	case CONTEXT_STF_IMPORT_MERGE_RIGHT:
+		delete_column (preview, col, FALSE);
+		break;
+	case CONTEXT_STF_IMPORT_SPLIT:
+		make_new_column (preview, col, preview->fixed_context_dx, FALSE);
+		break;
+	case CONTEXT_STF_IMPORT_WIDEN:
+		widen_column (preview, col, FALSE);
+		break;
+	case CONTEXT_STF_IMPORT_NARROW:
+		narrow_column (preview, col, FALSE);
+		break;
+	default:
+		; /* Nothing */
+	}
+	return TRUE;
+}
+
+static void
+fixed_context_menu (GncCsvPreview *preview, GdkEventButton *event,
+		    int col, int dx)
+{
+	int sensitivity_filter = 0;
+
+	preview->fixed_context_col = col;
+	preview->fixed_context_dx = dx;
+
+	if (!delete_column (preview, col - 1, TRUE))
+		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_LEFT);
+	if (!delete_column (preview, col, TRUE))
+		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_MERGE_RIGHT);
+	if (!make_new_column (preview, col, dx, TRUE))
+		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_SPLIT);
+	if (!widen_column (preview, col, TRUE))
+		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_WIDEN);
+	if (!narrow_column (preview, col, TRUE))
+		sensitivity_filter |= (1 << CONTEXT_STF_IMPORT_NARROW);
+
+	select_column (preview, col);
+	gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler,
+				    preview, 0,
+				    sensitivity_filter, event);
+}
+
+/* ---- End of Gnumeric Code ---- */
+
+/** Event handler for clicking on column headers. This function is
+ * called whenever the user clicks on column headers in
+ * preview->treeview to modify columns when in fixed-width mode.
+ * @param button The button at the top of a column of the treeview
+ * @param event The event that happened (where the user clicked)
+ * @param preview The data being configured
+ */
+/* TODO Comment */
+/* TODO Clean up */
+static void header_button_press_handler(GtkWidget* button, GdkEventButton* event,
+                                        GncCsvPreview* preview)
+{
+  /* col is the number of the column that was clicked, and offset is
+     to correct for the indentation of button. */
+  int i, col = 0, offset = GTK_BIN(button)->child->allocation.x - button->allocation.x,
+    ncols = preview->parse_data->column_types->len;
+  for(i = 0; i < ncols; i++)
+  {
+    if(preview->treeview_buttons[i] == button)
+    {
+      col = i;
+      break;
+    }
+  }
+
+  if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
+  {
+    make_new_column(preview, col, (int)event->x - offset, FALSE);
+  }
+  else if(event->type == GDK_BUTTON_PRESS && event->button == 3)
+  {
+    fixed_context_menu(preview, event, col, (int)event->x - offset);
+  }
+}
+
+/* Loads the preview's data into its data treeview. notEmpty is TRUE
+ * when the data treeview already contains data, FALSE otherwise
+ * (e.g. the first time this function is called on a preview).
+ * @param preview The data being previewed
+ * @param notEmpty Whether this function has been called before or not
+ */
 /* TODO Look at getting rid of notEmpty */
-static void gnc_csv_preview_treeview(GncCsvPreview* preview, gboolean notEmpty)
+static void gnc_csv_preview_update(GncCsvPreview* preview, gboolean notEmpty)
 {
   /* store has the data from the file being imported. cstores is an
    * array of stores that hold the combo box entries for each
@@ -474,7 +803,8 @@
   GtkListStore *store, **cstores, *ctstore;
   GtkTreeIter iter;
   /* ncols is the number of columns in the file data. */
-  int i, j, ncols = preview->parse_data->column_types->len;
+  int i, j, ncols = preview->parse_data->column_types->len,
+    max_str_len = preview->parse_data->file_str.end - preview->parse_data->file_str.begin;
 
   /* store contains only strings. */
   GType* types = g_new(GType, 2 * ncols);
@@ -526,23 +856,32 @@
     }
     g_list_free(tv_columns_begin);
     g_list_free(ctv_columns_begin);
+    g_free(preview->treeview_buttons);
   }
   
   /* Fill the data treeview with data from the file. */
+  /* Also, update the longest line value within the following loop (whichever is executed). */
+  preview->longest_line = 0;
   if(preview->previewing_errors) /* If we are showing only errors ... */
   {
     /* ... only pick rows that are in preview->error_lines. */
     GList* error_lines = preview->parse_data->error_lines;
     while(error_lines != NULL)
     {
+      int this_line_length = 0;
       i = GPOINTER_TO_INT(error_lines->data);
       gtk_list_store_append(store, &iter);
       for(j = 0; j < ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->len; j++)
       {
-        gtk_list_store_set(store, &iter, j,
-                           ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j],
-                           -1);
+        /* Add this cell's length to the row's length and set the value of the list store. */
+        gchar* cell_string = (gchar*)((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j];
+        this_line_length += g_utf8_strlen(cell_string, max_str_len);
+        gtk_list_store_set(store, &iter, j, cell_string, -1);
       }
+
+      if(this_line_length > preview->longest_line)
+        preview->longest_line = this_line_length;
+
       error_lines = g_list_next(error_lines);
     }
   }
@@ -550,15 +889,21 @@
   {
     for(i = 0; i < preview->parse_data->orig_lines->len; i++)
     {
+      int this_line_length = 0;
       gtk_list_store_append(store, &iter);
       for(j = 0; j < ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->len; j++)
       {
-        gtk_list_store_set(store, &iter, j,
-                           ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j],
-                           -1);
+        /* Add this cell's length to the row's length and set the value of the list store. */
+        gchar* cell_string = (gchar*)((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j];
+        this_line_length += g_utf8_strlen(cell_string, max_str_len);
+        gtk_list_store_set(store, &iter, j, cell_string, -1);
       }
+
+      if(this_line_length > preview->longest_line)
+        preview->longest_line = this_line_length;
     }
   }
+
   /* Set all the column types to what's in the parse data. */
   gtk_list_store_append(ctstore, &iter);
   for(i = 0; i < ncols; i++)
@@ -568,13 +913,17 @@
                        -1);
   }
 
+  preview->treeview_buttons = g_new(GtkWidget*, ncols);
   /* Insert columns into the data and column type treeviews. */
   for(i = 0; i < ncols; i++)
   {
+    GtkTreeViewColumn* col; /* The column we add to preview->treeview. */
     /* Create renderers for the data treeview (renderer) and the
      * column type treeview (crenderer). */
-    GtkCellRenderer *renderer = gtk_cell_renderer_text_new(),
+    GtkCellRenderer* renderer = gtk_cell_renderer_text_new(),
       *crenderer = gtk_cell_renderer_combo_new();
+    /* We want a monospace font for the data in case of fixed-width data. */
+    g_object_set(G_OBJECT(renderer), "family", "monospace", NULL);
     /* We are using cstores for the combo box entries, and we don't
      * want the user to be able to manually enter their own column
      * types. */
@@ -584,13 +933,20 @@
                      G_CALLBACK(column_type_edited), (gpointer)preview);
 
     /* Add a single column for the treeview. */
-    gtk_tree_view_insert_column_with_attributes(preview->treeview,
-                                                -1, "", renderer, "text", i, NULL);
+    col = gtk_tree_view_column_new_with_attributes("", renderer, "text", i, NULL);
+    gtk_tree_view_insert_column(preview->treeview, col, -1);
     /* Use the alternating model and text entries from ctstore in
      * preview->ctreeview. */
     gtk_tree_view_insert_column_with_attributes(preview->ctreeview,
                                                 -1, "", crenderer, "model", 2*i,
                                                 "text", 2*i+1, NULL);
+
+    /* We need to allow clicking on the column headers for fixed-width
+     * column splitting and merging. */
+    g_object_set(G_OBJECT(col), "clickable", TRUE, NULL);
+    g_signal_connect(G_OBJECT(col->button), "button_press_event",
+                     G_CALLBACK(header_button_press_handler), (gpointer)preview);
+    preview->treeview_buttons[i] = col->button;
   }
 
   /* Set the treeviews to use the models. */
@@ -632,9 +988,9 @@
   preview->approved = FALSE; /* This is FALSE until the user clicks "OK". */
 
   /* Load the data into the treeview. (This is the first time we've
-   * called gnc_csv_preview_treeview on this preview, so we use
-   * FALSE. */
-  gnc_csv_preview_treeview(preview, FALSE);
+   * called gnc_csv_preview_update on this preview, so we use
+   * FALSE.) */
+  gnc_csv_preview_update(preview, FALSE);
   /* Wait until the user clicks "OK" or "Cancel". */
   gtk_dialog_run(GTK_DIALOG(preview->dialog));
 
@@ -670,7 +1026,7 @@
   preview->approved = FALSE; /* This is FALSE until the user clicks "OK". */
 
   /* Wait until the user clicks "OK" or "Cancel". */
-  gnc_csv_preview_treeview(preview, TRUE);
+  gnc_csv_preview_update(preview, TRUE);
   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-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c	2007-07-25 06:53:58 UTC (rev 16356)
@@ -19,11 +19,13 @@
 
 static QofLogModule log_module = GNC_MOD_IMPORT;
 
-const int num_date_formats = 3;
+const int num_date_formats = 5;
 
-const gchar* date_format_user[] = {N_("y-d-m"),
+const gchar* date_format_user[] = {N_("y-m-d"),
                                    N_("d-m-y"),
-                                   N_("m-d-y")};
+                                   N_("m-d-y"),
+                                   N_("d-m"),
+                                   N_("m-d")};
 
 /** A set of sensible defaults for parsing CSV files. 
  * @return StfParseOptions_t* for parsing a file with comma separators
@@ -36,18 +38,11 @@
   return options;
 }
 
-/** Parses a string into a date, given a format. This function
- * requires only knowing the order in which the year, month and day
- * appear. For example, 01-02-2003 will be parsed the same way as
- * 01/02/2003.
- * @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(const char* date_str, int format)
+/* TODO Comment */
+static time_t parse_date_with_year(const char* date_str, int format)
 {
   time_t rawtime; /* The integer time */
-  struct tm retvalue; /* The time in a broken-down structure */
+  struct tm retvalue, test_retvalue; /* The time in a broken-down structure */
   
   int i, j, mem_length, orig_year, orig_month, orig_day;
 
@@ -72,6 +67,37 @@
   if(pmatch[0].rm_eo == 0)
     return -1;
 
+  /* If this is a string without separators ... */
+  if(pmatch[1].rm_so == -1)
+  {
+    /* ... we will fill in the indices based on the user's selection. */
+    int k = 0; /* k traverses date_str by keeping track of where separators "should" be. */
+    j = 1; /* j traverses pmatch. */
+    for(i = 0; date_format_user[format][i]; i++)
+    {
+      char segment_type = date_format_user[format][i];
+      /* Only do something if this is a meaningful character */
+      if(segment_type == 'y' || segment_type == 'm' || segment_type == 'd')
+      {
+        pmatch[j].rm_so = k;
+        switch(segment_type)
+        {
+        case 'm':
+        case 'd':
+          k += 2;
+          break;
+
+        case 'y':
+          k += 4;
+          break;
+        }
+
+        pmatch[j].rm_eo = k;
+        j++;
+      }
+    }    
+  }
+
   /* Put some sane values in retvalue by using the current time for
    * the non-year-month-day parts of the date. */
   time(&rawtime);
@@ -127,6 +153,13 @@
   }
   /* Convert back to an integer. If mktime leaves retvalue unchanged,
    * everything is okay; otherwise, an error has occurred. */
+  /* We have to use a "test" date value to account for changes in
+   * daylight savings time, which can cause a date change with mktime
+   * near midnight, causing the code to incorrectly think a date is
+   * incorrect. */
+  test_retvalue = retvalue;
+  mktime(&test_retvalue);
+  retvalue.tm_isdst = test_retvalue.tm_isdst;
   rawtime = mktime(&retvalue);
   if(retvalue.tm_mday == orig_day &&
      retvalue.tm_mon == orig_month &&
@@ -140,6 +173,114 @@
   }
 }
 
+/* TODO Comment */
+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;
+
+  /* Buffer for containing individual parts (e.g. year, month, day) of a date */
+  gchar* date_segment;
+
+  /* The compiled regular expression */
+  regex_t preg = {0};
+
+  /* An array containing indices specifying the matched substrings in date_str */
+  regmatch_t pmatch[3] = { {0}, {0}, {0} };
+
+  /* The regular expression for parsing dates */
+  const char* regex = "^ *([0-9]+) *[-/.'] *([0-9]+).*$";
+
+  /* We get our matches using the regular expression. */
+  regcomp(&preg, regex, REG_EXTENDED);
+  regexec(&preg, date_str, 3, pmatch, 0);
+  regfree(&preg);
+
+  /* If there wasn't a match, there was an error. */
+  if(pmatch[0].rm_eo == 0)
+    return -1;
+
+  /* Put some sane values in retvalue by using the current time for
+   * the non-year-month-day parts of the date. */
+  time(&rawtime);
+  localtime_r(&rawtime, &retvalue);
+  orig_year = retvalue.tm_year;
+
+  /* j traverses pmatch (index 0 contains the entire string, so we
+   * start at index 1 for the first meaningful match). */
+  j = 1;
+  /* Go through the date format and interpret the matches in order of
+   * the sections in the date format. */
+  for(i = 0; date_format_user[format][i]; i++)
+  {
+    char segment_type = date_format_user[format][i];
+    /* Only do something if this is a meaningful character */
+    if(segment_type == 'm' || segment_type == 'd')
+    {
+      /* Copy the matching substring into date_segment so that we can
+       * convert it into an integer. */
+      mem_length = pmatch[j].rm_eo - pmatch[j].rm_so;
+      date_segment = g_new(gchar, mem_length);
+      memcpy(date_segment, date_str + pmatch[j].rm_so, mem_length);
+      date_segment[mem_length] = '\0';
+
+      /* Set the appropriate member of retvalue. Save the original
+       * values so that we can check if the change when we use mktime
+       * below. */
+      switch(segment_type)
+      {
+      case 'm':
+        orig_month = retvalue.tm_mon = atoi(date_segment) - 1;
+        break;
+        
+      case 'd':
+        orig_day = retvalue.tm_mday = atoi(date_segment);
+        break;
+      }
+      g_free(date_segment);
+      j++;
+    }
+  }
+  /* Convert back to an integer. If mktime leaves retvalue unchanged,
+   * everything is okay; otherwise, an error has occurred. */
+  /* We have to use a "test" date value to account for changes in
+   * daylight savings time, which can cause a date change with mktime
+   * near midnight, causing the code to incorrectly think a date is
+   * incorrect. */
+  test_retvalue = retvalue;
+  mktime(&test_retvalue);
+  retvalue.tm_isdst = test_retvalue.tm_isdst;
+  rawtime = mktime(&retvalue);
+  if(retvalue.tm_mday == orig_day &&
+     retvalue.tm_mon == orig_month &&
+     retvalue.tm_year == orig_year)
+  {
+    return rawtime;
+  }
+  else
+  {
+    return -1;
+  }
+}
+
+/** Parses a string into a date, given a format. This function
+ * requires only knowing the order in which the year, month and day
+ * appear. For example, 01-02-2003 will be parsed the same way as
+ * 01/02/2003.
+ * @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(const char* date_str, int format)
+{
+  if(strchr(date_format_user[format], 'y'))
+    return parse_date_with_year(date_str, format);
+  else
+    return parse_date_without_year(date_str, format);
+}
+
 /** Constructor for GncCsvParseData.
  * @return Pointer to a new GncCSvParseData
  */
@@ -157,6 +298,7 @@
   parse_data->error_lines = parse_data->transactions = NULL;
   parse_data->options = default_parse_options();
   parse_data->date_format = -1;
+  parse_data->chunk = g_string_chunk_new(100 * 1024);
   return parse_data;
 }
 
@@ -198,6 +340,7 @@
     g_list_free(parse_data->transactions);
   }
 
+  g_free(parse_data->chunk);
   g_free(parse_data);
 }
 
@@ -306,21 +449,17 @@
 /* TODO Should we use 0 for domain and code in errors? */
 int gnc_csv_parse(GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)
 {
-  GStringChunk* chunk; /* TODO Find out exactly what this is. */
   /* max_cols is the number of columns in the row with the most columns. */
   int i, max_cols = 0;
 
   /* If everything is fine ... */
+  /* TODO Check about freeing parse_data->orig_lines ... */
   if(parse_data->file_str.begin != NULL)
   {
     /* Do the actual parsing. */
-    /* TODO: This size might have to change ... because I'm not exactly
-     * sure what it's for. ... */
-    chunk = g_string_chunk_new(100);
-    parse_data->orig_lines = stf_parse_general(parse_data->options, chunk,
+    parse_data->orig_lines = stf_parse_general(parse_data->options, parse_data->chunk,
                                                parse_data->file_str.begin,
                                                parse_data->file_str.end);
-    g_string_chunk_free(chunk);
   }
   /* If we couldn't get the encoding right, we just want an empty array. */
   else

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-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h	2007-07-25 06:53:58 UTC (rev 16356)
@@ -84,6 +84,7 @@
   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 */
+  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 */
   GList* error_lines; /**< List of row numbers in orig_lines that have errors */

Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade	2007-07-25 00:58:36 UTC (rev 16355)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade	2007-07-25 06:53:58 UTC (rev 16356)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Sun Jul  8 10:39:03 2007 by lasindi at pi-->
+<!--Generated with glade3 3.2.0 on Tue Jul 17 09:11:23 2007 by lasindi at pi-->
 <glade-interface>
   <widget class="GtkDialog" id="dialog">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -31,61 +31,39 @@
                   <placeholder/>
                 </child>
                 <child>
-                  <widget class="GtkLabel" id="label3">
+                  <widget class="GtkHSeparator" id="hseparator1">
                     <property name="visible">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="label" translatable="yes">Encoding: </property>
                   </widget>
                   <packing>
-                    <property name="x_options">GTK_FILL</property>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                    <property name="x_options">GTK_SHRINK | GTK_FILL</property>
                     <property name="y_options">GTK_FILL</property>
+                    <property name="y_padding">3</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkHSeparator" id="hseparator5">
+                  <widget class="GtkHSeparator" id="hseparator2">
                     <property name="visible">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                   </widget>
                   <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
                     <property name="x_options">GTK_SHRINK | GTK_FILL</property>
                     <property name="y_options">GTK_FILL</property>
                     <property name="y_padding">3</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkHSeparator" id="hseparator4">
-                    <property name="visible">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label1">
-                    <property name="visible">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="label" translatable="yes">Data type: </property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
                   <widget class="GtkHBox" id="hbox1">
                     <property name="visible">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <child>
-                      <widget class="GtkRadioButton" id="radiobutton">
+                      <widget class="GtkRadioButton" id="csv_button">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -102,7 +80,7 @@
                         <property name="label" translatable="yes">Fixed-Width</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">radiobutton</property>
+                        <property name="group">csv_button</property>
                       </widget>
                       <packing>
                         <property name="position">1</property>
@@ -118,33 +96,55 @@
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkHSeparator" id="hseparator2">
+                  <widget class="GtkLabel" id="label1">
                     <property name="visible">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">Data type: </property>
                   </widget>
                   <packing>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="x_options">GTK_FILL</property>
                     <property name="y_options">GTK_FILL</property>
-                    <property name="y_padding">3</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkHSeparator" id="hseparator1">
+                  <widget class="GtkHSeparator" id="hseparator4">
                     <property name="visible">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                   </widget>
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="right_attach">2</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHSeparator" id="hseparator5">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
                     <property name="x_options">GTK_SHRINK | GTK_FILL</property>
                     <property name="y_options">GTK_FILL</property>
                     <property name="y_padding">3</property>
                   </packing>
                 </child>
+                <child>
+                  <widget class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">Encoding: </property>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
               </widget>
               <packing>
                 <property name="expand">False</property>
@@ -171,63 +171,55 @@
                         <property name="column_spacing">3</property>
                         <property name="row_spacing">3</property>
                         <child>
-                          <widget class="GtkEntry" id="custom_entry">
+                          <widget class="GtkCheckButton" id="space_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="label" translatable="yes">Space</property>
+                            <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                            <property name="x_options">GTK_FILL</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="custom_cbutton">
+                          <widget class="GtkCheckButton" id="tab_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Custom</property>
+                            <property name="label" translatable="yes">Tab</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                            <property name="x_options">GTK_FILL</property>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="hyphen_cbutton">
+                          <widget class="GtkCheckButton" id="comma_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Hyphen (-)</property>
+                            <property name="label" translatable="yes">Comma (,)</property>
+                            <property name="active">True</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
                             <property name="left_attach">2</property>
                             <property name="right_attach">3</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="x_options">GTK_FILL</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="semicolon_cbutton">
+                          <widget class="GtkCheckButton" id="colon_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Semicolon (;)</property>
+                            <property name="label" translatable="yes">Colon (:)</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
                             <property name="top_attach">1</property>
                             <property name="bottom_attach">2</property>
                             <property name="x_options">GTK_FILL</property>
@@ -235,14 +227,16 @@
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="colon_cbutton">
+                          <widget class="GtkCheckButton" id="semicolon_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Colon (:)</property>
+                            <property name="label" translatable="yes">Semicolon (;)</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
                             <property name="top_attach">1</property>
                             <property name="bottom_attach">2</property>
                             <property name="x_options">GTK_FILL</property>
@@ -250,43 +244,49 @@
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="comma_cbutton">
+                          <widget class="GtkCheckButton" id="hyphen_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Comma (,)</property>
-                            <property name="active">True</property>
+                            <property name="label" translatable="yes">Hyphen (-)</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
                             <property name="left_attach">2</property>
                             <property name="right_attach">3</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options">GTK_FILL</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="tab_cbutton">
+                          <widget class="GtkCheckButton" id="custom_cbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Tab</property>
+                            <property name="label" translatable="yes">Custom</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options">GTK_FILL</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="space_cbutton">
+                          <widget class="GtkEntry" id="custom_entry">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Space</property>
-                            <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options">GTK_FILL</property>
                             <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>



More information about the gnucash-changes mailing list