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