mirror of https://github.com/Gnucash/gnucash
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/branches/1.8@8452 57a11ea4-9604-0410-9ed3-97b8803252fdzzzoldreleases/1.8
parent
80ad9bfeab
commit
698fce624f
@ -0,0 +1,888 @@
|
||||
/********************************************************************\
|
||||
* gnc-query-list.c -- A query display list. *
|
||||
* Copyright (C) 2003 Derek Atkins <derek@ihtfp.com> *
|
||||
* *
|
||||
* 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gnome.h>
|
||||
|
||||
#include "dialog-utils.h"
|
||||
#include "gnc-ui-util.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "gnc-component-manager.h"
|
||||
#include "messages.h"
|
||||
#include "gnc-query-list.h"
|
||||
#include "search-param.h"
|
||||
#include "QueryObject.h"
|
||||
|
||||
/* Signal codes */
|
||||
enum
|
||||
{
|
||||
LINE_TOGGLED,
|
||||
DOUBLE_CLICK_ENTRY,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
struct _GNCQueryListPriv {
|
||||
QueryAccess get_guid;
|
||||
gint component_id;
|
||||
};
|
||||
|
||||
/* Impossible to get at runtime. Assume this is a reasonable number */
|
||||
#define ARROW_SIZE 14
|
||||
#define VSCROLLBAR_SLOP 40
|
||||
|
||||
|
||||
/** Static Globals ****************************************************/
|
||||
static GtkCListClass *parent_class = NULL;
|
||||
static guint query_list_signals[LAST_SIGNAL] = {0};
|
||||
|
||||
|
||||
/** Static function declarations **************************************/
|
||||
static void gnc_query_list_init(GNCQueryList *list);
|
||||
static void gnc_query_list_init_clist (GNCQueryList *list);
|
||||
static void gnc_query_list_class_init(GNCQueryListClass *klass);
|
||||
static void gnc_query_list_select_row(GtkCList *clist, gint row,
|
||||
gint column, GdkEvent *event);
|
||||
static void gnc_query_list_unselect_row(GtkCList *clist, gint row,
|
||||
gint column, GdkEvent *event);
|
||||
static void gnc_query_list_destroy(GtkObject *object);
|
||||
static void gnc_query_list_fill(GNCQueryList *list);
|
||||
static void gnc_query_list_click_column_cb(GtkWidget *w, gint column,
|
||||
gpointer data);
|
||||
static void gnc_query_list_size_allocate_cb(GtkWidget *w,
|
||||
GtkAllocation *allocation,
|
||||
gpointer data);
|
||||
|
||||
static void gnc_query_list_set_query_sort (GNCQueryList *list, gboolean new_column);
|
||||
|
||||
GtkType
|
||||
gnc_query_list_get_type (void)
|
||||
{
|
||||
static GtkType gnc_query_list_type = 0;
|
||||
|
||||
if (!gnc_query_list_type)
|
||||
{
|
||||
static const GtkTypeInfo gnc_query_list_info =
|
||||
{
|
||||
"GNCQueryList",
|
||||
sizeof (GNCQueryList),
|
||||
sizeof (GNCQueryListClass),
|
||||
(GtkClassInitFunc) gnc_query_list_class_init,
|
||||
(GtkObjectInitFunc) gnc_query_list_init,
|
||||
/* reserved_1 */ NULL,
|
||||
/* reserved_2 */ NULL,
|
||||
(GtkClassInitFunc) NULL
|
||||
};
|
||||
|
||||
gnc_query_list_type = gtk_type_unique(GTK_TYPE_CLIST,
|
||||
&gnc_query_list_info);
|
||||
}
|
||||
|
||||
return gnc_query_list_type;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_new *
|
||||
* creates the query list *
|
||||
* *
|
||||
* Args: param_list - the list of params *
|
||||
* query - the query to use to find entries *
|
||||
* Returns: the query list widget, or NULL if there was a problem. *
|
||||
\********************************************************************/
|
||||
void
|
||||
gnc_query_list_construct (GNCQueryList *list, GList *param_list, Query *query)
|
||||
{
|
||||
g_return_if_fail(list);
|
||||
g_return_if_fail(param_list);
|
||||
g_return_if_fail(query);
|
||||
g_return_if_fail(IS_GNC_QUERY_LIST(list));
|
||||
|
||||
/* more configuration */
|
||||
list->query = gncQueryCopy(query);
|
||||
list->column_params = param_list;
|
||||
|
||||
/* cache the function to get the guid of this query type */
|
||||
list->priv->get_guid =
|
||||
gncQueryObjectGetParameterGetter (gncQueryGetSearchFor (query),
|
||||
QUERY_PARAM_GUID);
|
||||
|
||||
/* Initialize the CList */
|
||||
gnc_query_list_init_clist(list);
|
||||
}
|
||||
|
||||
|
||||
GtkWidget *
|
||||
gnc_query_list_new(GList *param_list, Query *query)
|
||||
{
|
||||
GNCQueryList *list;
|
||||
|
||||
g_return_val_if_fail(param_list, NULL);
|
||||
g_return_val_if_fail(query, NULL);
|
||||
|
||||
list = GNC_QUERY_LIST(gtk_type_new(gnc_query_list_get_type()));
|
||||
|
||||
gnc_query_list_construct(list, param_list, query);
|
||||
|
||||
return GTK_WIDGET(list);
|
||||
}
|
||||
|
||||
void gnc_query_list_reset_query (GNCQueryList *list, Query *query)
|
||||
{
|
||||
g_return_if_fail(list);
|
||||
g_return_if_fail(query);
|
||||
g_return_if_fail (IS_GNC_QUERY_LIST(list));
|
||||
|
||||
gncQueryDestroy(list->query);
|
||||
list->query = gncQueryCopy(query);
|
||||
gnc_query_list_set_query_sort(list, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
update_booleans (GNCQueryList *list, gint row)
|
||||
{
|
||||
GtkCList *clist = GTK_CLIST(list);
|
||||
gpointer entry;
|
||||
GList *node;
|
||||
gint i;
|
||||
gboolean result;
|
||||
|
||||
entry = gtk_clist_get_row_data (clist, row);
|
||||
for (i = 0, node = list->column_params; node; node = node->next, i++)
|
||||
{
|
||||
GNCSearchParam *param = node->data;
|
||||
const char *type = gnc_search_param_get_param_type (param);
|
||||
|
||||
/* if this is a boolean, ignore it now -- we'll use a checkmark later */
|
||||
if (safe_strcmp (type, QUERYCORE_BOOLEAN))
|
||||
continue;
|
||||
|
||||
result = (gboolean)(gnc_search_param_compute_value(param, entry));
|
||||
gnc_clist_set_check (clist, row, i, result);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_column_title (GNCQueryList *list, gint column, const gchar *title)
|
||||
{
|
||||
GtkWidget *hbox, *label, *arrow;
|
||||
|
||||
hbox = gtk_hbox_new(FALSE, 2);
|
||||
gtk_widget_show(hbox);
|
||||
gtk_clist_set_column_widget(GTK_CLIST(list), column, hbox);
|
||||
|
||||
label = gtk_label_new(title);
|
||||
gtk_widget_show(label);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||
|
||||
arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_ETCHED_IN);
|
||||
list->title_arrows[column] = arrow;
|
||||
if (column == 0)
|
||||
gtk_widget_show(arrow);
|
||||
gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_refresh_handler (GHashTable *changes, gpointer user_data)
|
||||
{
|
||||
GNCQueryList *list = (GNCQueryList *)user_data;
|
||||
g_return_if_fail (IS_GNC_QUERY_LIST(list));
|
||||
|
||||
gnc_query_list_refresh (list);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_init (GNCQueryList *list)
|
||||
{
|
||||
list->query = NULL;
|
||||
list->no_toggle = FALSE;
|
||||
list->always_unselect = FALSE;
|
||||
|
||||
list->num_columns = 0;
|
||||
list->column_params = NULL;
|
||||
|
||||
list->sort_column = 0;
|
||||
list->increasing = TRUE;
|
||||
list->title_arrows = NULL;
|
||||
|
||||
list->prev_allocation = -1;
|
||||
list->title_widths = NULL;
|
||||
|
||||
list->numeric_abs = FALSE;
|
||||
list->numeric_inv_sort = FALSE;
|
||||
|
||||
list->priv = g_new0(GNCQueryListPriv, 1);
|
||||
list->priv->component_id =
|
||||
gnc_register_gui_component ("gnc-query-list-cm-class",
|
||||
gnc_query_list_refresh_handler,
|
||||
NULL, list);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_init_clist (GNCQueryList *list)
|
||||
{
|
||||
GtkCList *clist = GTK_CLIST (list);
|
||||
GtkStyle *style;
|
||||
GList *node;
|
||||
gchar **titles;
|
||||
gint i;
|
||||
|
||||
/* compute the number of columns and fill in the rest of the list */
|
||||
list->num_columns = g_list_length(list->column_params);
|
||||
list->title_arrows = g_new0(GtkWidget*, list->num_columns);
|
||||
list->title_widths = g_new0(gint, list->num_columns);
|
||||
|
||||
/* build an array of titles */
|
||||
titles = g_new0(gchar*, list->num_columns);
|
||||
for (i = 0, node = list->column_params; node; node = node->next, i++) {
|
||||
GNCSearchParam *param = node->data;
|
||||
titles[i] = (gchar *)param->title;
|
||||
}
|
||||
|
||||
/* construct the clist */
|
||||
gtk_clist_construct (clist, list->num_columns, titles);
|
||||
gtk_clist_set_shadow_type (clist, GTK_SHADOW_IN);
|
||||
|
||||
/* build all the column titles */
|
||||
for (i = 0; i < list->num_columns; i++)
|
||||
gnc_query_list_column_title(list, i, titles[i]);
|
||||
|
||||
/* set the column justification */
|
||||
for (i = 0, node = list->column_params; node; node = node->next, i++) {
|
||||
GNCSearchParam *param = node->data;
|
||||
gtk_clist_set_column_justification (clist, i, param->justify);
|
||||
|
||||
if (param->passive)
|
||||
gtk_clist_column_title_passive (clist, i);
|
||||
|
||||
if (param->non_resizeable)
|
||||
gtk_clist_set_column_resizeable (clist, i, FALSE);
|
||||
}
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (clist), "click_column",
|
||||
GTK_SIGNAL_FUNC(gnc_query_list_click_column_cb),
|
||||
NULL);
|
||||
gtk_signal_connect (GTK_OBJECT (clist), "size_allocate",
|
||||
GTK_SIGNAL_FUNC(gnc_query_list_size_allocate_cb),
|
||||
NULL);
|
||||
|
||||
style = gtk_widget_get_style (GTK_WIDGET(list));
|
||||
|
||||
{
|
||||
GdkFont *font = NULL;
|
||||
gint width;
|
||||
|
||||
font = style->font;
|
||||
if (font != NULL)
|
||||
{
|
||||
for (i = 0, node = list->column_params; node; node = node->next, i++)
|
||||
{
|
||||
GNCSearchParam *param = node->data;
|
||||
width = gdk_string_width (font, titles[i]) + 5;
|
||||
if (!param->passive)
|
||||
width += ARROW_SIZE;
|
||||
gtk_clist_set_column_min_width (clist, i, width);
|
||||
list->title_widths[i] = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_free(titles);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_class_init (GNCQueryListClass *klass)
|
||||
{
|
||||
GtkObjectClass *object_class;
|
||||
GtkWidgetClass *widget_class;
|
||||
GtkContainerClass *container_class;
|
||||
GtkCListClass *clist_class;
|
||||
|
||||
object_class = (GtkObjectClass*) klass;
|
||||
widget_class = (GtkWidgetClass*) klass;
|
||||
container_class = (GtkContainerClass*) klass;
|
||||
clist_class = (GtkCListClass*) klass;
|
||||
|
||||
parent_class = gtk_type_class(GTK_TYPE_CLIST);
|
||||
|
||||
query_list_signals[LINE_TOGGLED] =
|
||||
gtk_signal_new("line_toggled",
|
||||
GTK_RUN_FIRST,
|
||||
object_class->type,
|
||||
GTK_SIGNAL_OFFSET(GNCQueryListClass,
|
||||
line_toggled),
|
||||
gtk_marshal_NONE__POINTER,
|
||||
GTK_TYPE_NONE, 1,
|
||||
GTK_TYPE_POINTER);
|
||||
|
||||
query_list_signals[DOUBLE_CLICK_ENTRY] =
|
||||
gtk_signal_new("double_click_entry",
|
||||
GTK_RUN_FIRST,
|
||||
object_class->type,
|
||||
GTK_SIGNAL_OFFSET(GNCQueryListClass,
|
||||
double_click_entry),
|
||||
gtk_marshal_NONE__POINTER,
|
||||
GTK_TYPE_NONE, 1,
|
||||
GTK_TYPE_POINTER);
|
||||
|
||||
gtk_object_class_add_signals(object_class,
|
||||
query_list_signals,
|
||||
LAST_SIGNAL);
|
||||
|
||||
object_class->destroy = gnc_query_list_destroy;
|
||||
|
||||
clist_class->select_row = gnc_query_list_select_row;
|
||||
clist_class->unselect_row = gnc_query_list_unselect_row;
|
||||
|
||||
klass->line_toggled = NULL;
|
||||
klass->double_click_entry = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_toggle (GNCQueryList *list)
|
||||
{
|
||||
gpointer entry;
|
||||
gint row;
|
||||
|
||||
g_return_if_fail (IS_GNC_QUERY_LIST(list));
|
||||
|
||||
if (list->no_toggle)
|
||||
return;
|
||||
|
||||
row = list->current_row;
|
||||
entry = gtk_clist_get_row_data (GTK_CLIST(list), row);
|
||||
list->current_entry = entry;
|
||||
|
||||
gtk_signal_emit (GTK_OBJECT (list), query_list_signals[LINE_TOGGLED], entry);
|
||||
|
||||
update_booleans (list, row);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_select_row (GtkCList *clist, gint row, gint column,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GNCQueryList *list = GNC_QUERY_LIST(clist);
|
||||
|
||||
list->current_row = row;
|
||||
|
||||
gnc_query_list_toggle (list);
|
||||
if (event == NULL) {
|
||||
/* User pressed the space key */
|
||||
parent_class->scroll_vertical(clist, GTK_SCROLL_STEP_FORWARD, 0.0);
|
||||
}
|
||||
|
||||
/* This will trigger an unselect event for the currently selected row */
|
||||
parent_class->select_row (clist, row, column, event);
|
||||
|
||||
if (event && (event->type == GDK_2BUTTON_PRESS))
|
||||
{
|
||||
gpointer entry;
|
||||
|
||||
entry = gtk_clist_get_row_data (clist, row);
|
||||
|
||||
gtk_signal_emit(GTK_OBJECT(list),
|
||||
query_list_signals[DOUBLE_CLICK_ENTRY], entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_unselect_row (GtkCList *clist, gint row, gint column,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GNCQueryList *list = GNC_QUERY_LIST(clist);
|
||||
|
||||
if (row == list->current_row)
|
||||
{
|
||||
gnc_query_list_toggle (list);
|
||||
if (event == NULL) {
|
||||
/* User pressed the space key */
|
||||
parent_class->scroll_vertical(clist, GTK_SCROLL_STEP_FORWARD, 0.0);
|
||||
}
|
||||
if (!list->always_unselect)
|
||||
return;
|
||||
}
|
||||
|
||||
parent_class->unselect_row (clist, row, column, event);
|
||||
|
||||
if (event && (event->type == GDK_2BUTTON_PRESS))
|
||||
{
|
||||
gpointer entry;
|
||||
|
||||
entry = gtk_clist_get_row_data (clist, row);
|
||||
|
||||
gtk_signal_emit (GTK_OBJECT(list),
|
||||
query_list_signals[DOUBLE_CLICK_ENTRY], entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_destroy (GtkObject *object)
|
||||
{
|
||||
GNCQueryList *list = GNC_QUERY_LIST(object);
|
||||
|
||||
if (list->priv && list->priv->component_id >= 0)
|
||||
gnc_unregister_gui_component (list->priv->component_id);
|
||||
if (list->priv)
|
||||
{
|
||||
g_free (list->priv);
|
||||
list->priv = NULL;
|
||||
}
|
||||
if (list->query)
|
||||
{
|
||||
xaccFreeQuery(list->query);
|
||||
list->query = NULL;
|
||||
}
|
||||
if (list->column_params)
|
||||
{
|
||||
/* XXX: free the params list??? */
|
||||
}
|
||||
if (list->title_arrows)
|
||||
{
|
||||
g_free(list->title_arrows);
|
||||
list->title_arrows = NULL;
|
||||
}
|
||||
if (list->title_widths)
|
||||
{
|
||||
g_free(list->title_widths);
|
||||
list->title_widths = NULL;
|
||||
}
|
||||
|
||||
if (GTK_OBJECT_CLASS(parent_class)->destroy)
|
||||
GTK_OBJECT_CLASS(parent_class)->destroy (object);
|
||||
}
|
||||
|
||||
gint
|
||||
gnc_query_list_get_needed_height (GNCQueryList *list, gint num_rows)
|
||||
{
|
||||
GtkCList *clist;
|
||||
gint list_height;
|
||||
gint title_height;
|
||||
|
||||
g_return_val_if_fail (list != NULL, 0);
|
||||
g_return_val_if_fail (IS_GNC_QUERY_LIST(list), 0);
|
||||
|
||||
if (!GTK_WIDGET_REALIZED (list))
|
||||
return 0;
|
||||
|
||||
clist = GTK_CLIST (list);
|
||||
|
||||
/* sync with gtkclist.c */
|
||||
title_height = (clist->column_title_area.height +
|
||||
(GTK_WIDGET(list)->style->klass->ythickness +
|
||||
GTK_CONTAINER(list)->border_width) * 2);
|
||||
list_height = (clist->row_height * num_rows) + (num_rows + 1);
|
||||
|
||||
return title_height + list_height;
|
||||
}
|
||||
|
||||
gint
|
||||
gnc_query_list_get_num_entries (GNCQueryList *list)
|
||||
{
|
||||
g_return_val_if_fail (list != NULL, 0);
|
||||
g_return_val_if_fail (IS_GNC_QUERY_LIST(list), 0);
|
||||
|
||||
return list->num_entries;
|
||||
}
|
||||
|
||||
gpointer
|
||||
gnc_query_list_get_current_entry (GNCQueryList *list)
|
||||
{
|
||||
g_return_val_if_fail (list != NULL, NULL);
|
||||
g_return_val_if_fail (IS_GNC_QUERY_LIST(list), NULL);
|
||||
|
||||
return list->current_entry;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_recompute_widths *
|
||||
* Given a new widget width, recompute the widths of each column. *
|
||||
* Give any excess allocation to the description field. This also *
|
||||
* handles the case of allocating column widths when the list is *
|
||||
* first filled with data. *
|
||||
* *
|
||||
* Args: list - a GncQueryList widget *
|
||||
* allocated - the allocated width for this list *
|
||||
* Returns: nothing *
|
||||
\********************************************************************/
|
||||
static void
|
||||
gnc_query_list_recompute_widths (GNCQueryList *list, gint allocated)
|
||||
{
|
||||
GtkCList *clist = GTK_CLIST(list);
|
||||
gint total_width, desc_width = 0, excess, i;
|
||||
|
||||
/* Prevent loops when allocation is bigger than total widths */
|
||||
if (allocated == list->prev_allocation)
|
||||
return;
|
||||
|
||||
/* Enforce column minimum widths */
|
||||
total_width = 0;
|
||||
for (i = 0; i < list->num_columns; i++)
|
||||
{
|
||||
gint width;
|
||||
|
||||
width = gtk_clist_optimal_column_width(clist, i);
|
||||
if (width < list->title_widths[i])
|
||||
width = list->title_widths[i];
|
||||
total_width += width;
|
||||
gtk_clist_set_column_width (clist, i, width);
|
||||
if (i == 2)
|
||||
desc_width = width;
|
||||
}
|
||||
|
||||
/* Did the list use its full allocation?
|
||||
*
|
||||
* Add/subtract any underage/overage to/from the description column
|
||||
*/
|
||||
if (allocated <= 1)
|
||||
allocated = list->prev_allocation;
|
||||
list->prev_allocation = allocated;
|
||||
excess = allocated - total_width - VSCROLLBAR_SLOP;
|
||||
|
||||
/* XXX: Choose a generic column to resize */
|
||||
gtk_clist_set_column_width (clist, 2, desc_width + excess);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_size_allocate_cb *
|
||||
* The allocated size has changed. Need to recompute the *
|
||||
* column widths *
|
||||
* *
|
||||
* Args: w - a GncQueryList widget *
|
||||
* allocation - a widget allocation desctiption *
|
||||
* data - unused *
|
||||
* Returns: nothing *
|
||||
\********************************************************************/
|
||||
static void
|
||||
gnc_query_list_size_allocate_cb (GtkWidget *w,
|
||||
GtkAllocation *allocation,
|
||||
gpointer data)
|
||||
{
|
||||
GNCQueryList *list = GNC_QUERY_LIST(w);
|
||||
|
||||
g_return_if_fail (list != NULL);
|
||||
gnc_query_list_recompute_widths(list, allocation->width);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_refresh *
|
||||
* refreshes the list *
|
||||
* *
|
||||
* Args: list - list to refresh *
|
||||
* Returns: nothing *
|
||||
\********************************************************************/
|
||||
void
|
||||
gnc_query_list_refresh (GNCQueryList *list)
|
||||
{
|
||||
GtkCList *clist = GTK_CLIST(list);
|
||||
GtkAdjustment *adjustment;
|
||||
gfloat save_value = 0.0;
|
||||
gpointer *old_focus_entry;
|
||||
gpointer *old_entry;
|
||||
gint old_focus_row;
|
||||
gint new_row;
|
||||
|
||||
g_return_if_fail (list != NULL);
|
||||
g_return_if_fail (IS_GNC_QUERY_LIST(list));
|
||||
|
||||
adjustment = gtk_clist_get_vadjustment (GTK_CLIST(list));
|
||||
if (adjustment != NULL)
|
||||
save_value = adjustment->value;
|
||||
|
||||
old_focus_row = clist->focus_row;
|
||||
old_focus_entry = gtk_clist_get_row_data (clist, old_focus_row);
|
||||
|
||||
gtk_clist_freeze (clist);
|
||||
gtk_clist_clear (clist);
|
||||
|
||||
old_entry = list->current_entry;
|
||||
list->num_entries = 0;
|
||||
list->current_row = -1;
|
||||
list->current_entry = NULL;
|
||||
|
||||
gnc_query_list_fill (list);
|
||||
|
||||
gnc_query_list_recompute_widths (list, -1);
|
||||
|
||||
if (adjustment)
|
||||
{
|
||||
save_value = CLAMP (save_value, adjustment->lower, adjustment->upper);
|
||||
gtk_adjustment_set_value (adjustment, save_value);
|
||||
}
|
||||
|
||||
if (old_entry)
|
||||
{
|
||||
new_row = gtk_clist_find_row_from_data (clist, old_entry);
|
||||
if (new_row >= 0)
|
||||
{
|
||||
list->no_toggle = TRUE;
|
||||
gtk_clist_select_row (clist, new_row, 0);
|
||||
list->no_toggle = FALSE;
|
||||
list->current_entry = old_entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_focus_entry)
|
||||
{
|
||||
new_row = gtk_clist_find_row_from_data (clist, old_focus_entry);
|
||||
|
||||
if (new_row < 0)
|
||||
new_row = old_focus_row;
|
||||
|
||||
if (new_row >= 0)
|
||||
clist->focus_row = new_row;
|
||||
}
|
||||
|
||||
gtk_clist_thaw (clist);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_set_query_sort *
|
||||
* sets the sorting order of entries in the list *
|
||||
* *
|
||||
* Args: list - list to change the sort order for *
|
||||
* new_column - is this a new column (so should we set the *
|
||||
* query sort order or just set the 'increasing' *
|
||||
* Returns: nothing *
|
||||
\********************************************************************/
|
||||
static void
|
||||
gnc_query_list_set_query_sort (GNCQueryList *list, gboolean new_column)
|
||||
{
|
||||
gboolean sort_order = list->increasing;
|
||||
GList *node;
|
||||
GNCSearchParam *param;
|
||||
|
||||
/* Find the column parameter definition */
|
||||
node = g_list_nth(list->column_params, list->sort_column);
|
||||
param = node->data;
|
||||
|
||||
/* If we're asked to invert numerics, and if this is a numeric or
|
||||
* debred column, then invert the sort order.
|
||||
*/
|
||||
if (list->numeric_inv_sort) {
|
||||
const char *type = gnc_search_param_get_param_type (param);
|
||||
if (!safe_strcmp(type, QUERYCORE_NUMERIC) ||
|
||||
!safe_strcmp(type, QUERYCORE_DEBCRED))
|
||||
sort_order = !sort_order;
|
||||
}
|
||||
|
||||
/* Set the sort order for the engine, if the key changed */
|
||||
if (new_column)
|
||||
{
|
||||
GSList *p1, *p2;
|
||||
|
||||
p1 = gnc_search_param_get_param_path(param);
|
||||
p2 = g_slist_prepend(NULL, QUERY_DEFAULT_SORT);
|
||||
gncQuerySetSortOrder (list->query, p1, p2, NULL);
|
||||
}
|
||||
|
||||
xaccQuerySetSortIncreasing (list->query,
|
||||
sort_order,
|
||||
sort_order,
|
||||
sort_order);
|
||||
|
||||
/*
|
||||
* Recompute the list. Is this really necessary? Why not just sort
|
||||
* the rows already in the clist? Answer: it would be an n-squared
|
||||
* algorithm to get the clist to match the resulting list.
|
||||
*/
|
||||
gnc_query_list_refresh(list);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_set_sort_column *
|
||||
* sets the sorting order of entries in the list *
|
||||
* *
|
||||
* Args: list - list to change the sort order for *
|
||||
* column - the column to sort on *
|
||||
* Returns: nothing *
|
||||
\********************************************************************/
|
||||
static void
|
||||
gnc_query_list_set_sort_column (GNCQueryList *list, gint sort_column)
|
||||
{
|
||||
gint column;
|
||||
gboolean new_column = FALSE;
|
||||
|
||||
g_return_if_fail (list != NULL);
|
||||
g_return_if_fail (IS_GNC_QUERY_LIST(list));
|
||||
g_return_if_fail (list->query != NULL);
|
||||
|
||||
/* Clear all arrows */
|
||||
for (column = 0; column < list->num_columns; column++)
|
||||
{
|
||||
if (list->title_arrows[column])
|
||||
gtk_widget_hide(list->title_arrows[column]);
|
||||
}
|
||||
|
||||
/* Is this a new column or a re-click on the existing column? */
|
||||
column = sort_column;
|
||||
new_column = (list->sort_column != sort_column);
|
||||
|
||||
list->increasing = new_column ? TRUE : !list->increasing;
|
||||
list->sort_column = sort_column;
|
||||
|
||||
/* Set the appropriate arrow */
|
||||
gtk_arrow_set(GTK_ARROW(list->title_arrows[column]),
|
||||
list->increasing ? GTK_ARROW_DOWN : GTK_ARROW_UP,
|
||||
GTK_SHADOW_ETCHED_IN);
|
||||
gtk_widget_show(list->title_arrows[column]);
|
||||
|
||||
gnc_query_list_set_query_sort (list, new_column);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_click_column_cb(GtkWidget *w, gint column, gpointer data)
|
||||
{
|
||||
GNCQueryList *list = GNC_QUERY_LIST(w);
|
||||
gnc_query_list_set_sort_column(list, column);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_query_list_fill(GNCQueryList *list)
|
||||
{
|
||||
gchar *strings[list->num_columns + 1];
|
||||
GList *entries, *item;
|
||||
const GUID *guid;
|
||||
gint i;
|
||||
|
||||
/* Clear all watches */
|
||||
gnc_gui_component_clear_watches (list->priv->component_id);
|
||||
|
||||
/* Reverse the list now because 'append()' takes too long */
|
||||
entries = gncQueryRun(list->query);
|
||||
|
||||
for (item = entries; item; item = item->next)
|
||||
{
|
||||
GList *node;
|
||||
gint row;
|
||||
|
||||
for (i = 0, node = list->column_params; node; node = node->next)
|
||||
{
|
||||
GNCSearchParam *param = node->data;
|
||||
GSList *converters = gnc_search_param_get_converters (param);
|
||||
const char *type = gnc_search_param_get_param_type (param);
|
||||
gpointer res = item->data;
|
||||
QueryAccess fcn = NULL;
|
||||
|
||||
/* if this is a boolean, ignore it now -- we'll use a checkmark later */
|
||||
if (!safe_strcmp (type, QUERYCORE_BOOLEAN)) {
|
||||
strings[i++] = g_strdup("");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do all the object conversions */
|
||||
for (; converters; converters = converters->next) {
|
||||
fcn = converters->data;
|
||||
|
||||
if (converters->next)
|
||||
res = fcn (res);
|
||||
}
|
||||
|
||||
/* Now convert this to a text value for the row */
|
||||
if (!safe_strcmp(type, QUERYCORE_DEBCRED) ||
|
||||
!safe_strcmp(type, QUERYCORE_NUMERIC))
|
||||
{
|
||||
gnc_numeric (*nfcn)(gpointer) = (gnc_numeric(*)(gpointer))fcn;
|
||||
gnc_numeric value = nfcn(res);
|
||||
if (list->numeric_abs)
|
||||
value = gnc_numeric_abs (value);
|
||||
strings[i++] = g_strdup(xaccPrintAmount(value,gnc_default_print_info(FALSE)));
|
||||
} else
|
||||
strings[i++] = gncQueryCoreToString (type, res, fcn);
|
||||
}
|
||||
|
||||
row = gtk_clist_append (GTK_CLIST(list), (gchar **) strings);
|
||||
gtk_clist_set_row_data (GTK_CLIST(list), row, item->data);
|
||||
|
||||
/* Free up our strings */
|
||||
for (i = 0; i < list->num_columns; i++) {
|
||||
if (strings[i])
|
||||
g_free (strings[i]);
|
||||
}
|
||||
|
||||
/* Now update any checkmarks */
|
||||
update_booleans (list, row);
|
||||
|
||||
/* and set a watcher on this item */
|
||||
guid = (const GUID*)((list->priv->get_guid)(item->data));
|
||||
gnc_gui_component_watch_entity (list->priv->component_id, guid,
|
||||
GNC_EVENT_MODIFY | GNC_EVENT_DESTROY);
|
||||
|
||||
list->num_entries++;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* gnc_query_list_unselect_all *
|
||||
* unselect all items in the list *
|
||||
* *
|
||||
* Args: list - list to unselect all *
|
||||
* Returns: nothing *
|
||||
\********************************************************************/
|
||||
void
|
||||
gnc_query_list_unselect_all(GNCQueryList *list)
|
||||
{
|
||||
g_return_if_fail (list != NULL);
|
||||
g_return_if_fail (IS_GNC_QUERY_LIST(list));
|
||||
|
||||
list->no_toggle = TRUE;
|
||||
list->always_unselect = TRUE;
|
||||
|
||||
gtk_clist_unselect_all (GTK_CLIST(list));
|
||||
|
||||
list->always_unselect = FALSE;
|
||||
list->no_toggle = FALSE;
|
||||
|
||||
list->current_entry = NULL;
|
||||
}
|
||||
|
||||
|
||||
gboolean gnc_query_list_item_in_list (GNCQueryList *list, gpointer item)
|
||||
{
|
||||
g_return_val_if_fail(list, FALSE);
|
||||
g_return_val_if_fail(item, FALSE);
|
||||
g_return_val_if_fail(IS_GNC_QUERY_LIST(list), FALSE);
|
||||
|
||||
return (gtk_clist_find_row_from_data(GTK_CLIST(list), item) != -1);
|
||||
}
|
||||
|
||||
void gnc_query_list_refresh_item (GNCQueryList *list, gpointer item)
|
||||
{
|
||||
gint row;
|
||||
|
||||
g_return_if_fail(list);
|
||||
g_return_if_fail(item);
|
||||
g_return_if_fail(IS_GNC_QUERY_LIST(list));
|
||||
|
||||
row = gtk_clist_find_row_from_data(GTK_CLIST(list), item);
|
||||
if (row != -1)
|
||||
update_booleans (list, row);
|
||||
}
|
||||
|
||||
void
|
||||
gnc_query_list_set_numerics (GNCQueryList *list, gboolean abs, gboolean inv_sort)
|
||||
{
|
||||
g_return_if_fail(list);
|
||||
g_return_if_fail(IS_GNC_QUERY_LIST(list));
|
||||
|
||||
list->numeric_abs = abs;
|
||||
list->numeric_inv_sort = inv_sort;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
*.lo
|
||||
*.la
|
||||
.deps
|
||||
.libs
|
||||
Makefile
|
||||
Makefile.in
|
||||
@ -0,0 +1,44 @@
|
||||
SUBDIRS = .
|
||||
|
||||
pkglib_LTLIBRARIES=libgncmod-log-replay.la
|
||||
|
||||
libgncmod_log_replay_la_SOURCES = \
|
||||
gnc-log-replay.c \
|
||||
gncmod-log-replay.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
gnc-log-replay.h
|
||||
|
||||
libgncmod_log_replay_la_LDFLAGS = -module
|
||||
|
||||
libgncmod_log_replay_la_LIBADD = \
|
||||
${top_builddir}/src/gnc-module/libgncmodule.la \
|
||||
${top_builddir}/src/engine/libgncmod-engine.la \
|
||||
${top_builddir}/src/import-export/libgncmod-generic-import.la \
|
||||
${GLIB_LIBS}
|
||||
|
||||
gncscmdir = ${GNC_SCM_INSTALL_DIR}/log-replay
|
||||
|
||||
gncscm_DATA = \
|
||||
log-replay.scm
|
||||
|
||||
AM_CFLAGS = \
|
||||
-I${top_srcdir}/src \
|
||||
-I${top_srcdir}/src/engine \
|
||||
-I${top_srcdir}/src/gnc-module \
|
||||
-I${top_srcdir}/src/app-utils \
|
||||
-I${top_srcdir}/src/app-file \
|
||||
-I${top_srcdir}/src/gnome \
|
||||
-I${top_srcdir}/src/gnome-utils \
|
||||
-I${top_srcdir}/src/import-export \
|
||||
${GNOME_INCLUDEDIR} \
|
||||
${GTKHTML_CFLAGS} \
|
||||
${GLADE_CFLAGS} \
|
||||
${GUILE_INCS} \
|
||||
${GLIB_CFLAGS}
|
||||
|
||||
EXTRA_DIST = \
|
||||
.cvsignore \
|
||||
${gncscm_DATA}
|
||||
|
||||
CLEANFILES = g-wrapped .scm-links
|
||||
@ -0,0 +1,573 @@
|
||||
/********************************************************************\
|
||||
* 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
/** @addtogroup Import_Export
|
||||
@{ */
|
||||
/** @internal
|
||||
@file gnc-log-replay.c
|
||||
@brief .log file replay code
|
||||
@author Copyright (c) 2003 Benoit Grégoire <bock@step.polymtl.ca>
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libguile.h>
|
||||
#include <gmodule.h>
|
||||
|
||||
#include "Account.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionP.h"
|
||||
#include "global-options.h"
|
||||
#include "gnc-log-replay.h"
|
||||
#include "gnc-file-dialog.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "gnc-book.h"
|
||||
#include "gnc-ui-util.h"
|
||||
|
||||
#include "dialog-utils.h"
|
||||
|
||||
|
||||
/*static short module = MOD_IMPORT;*/
|
||||
static short module = MOD_TEST;
|
||||
|
||||
/* fprintf (trans_log, "mod guid time_now " \
|
||||
"date_entered date_posted " \
|
||||
"acc_guid acc_name num description " \
|
||||
"memo action reconciled " \
|
||||
"amount value date_reconciled\n");
|
||||
"%c\t%s/%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
|
||||
"%s\t%s\t%s\t%c\t%lld/%lld\t%lld/%lld\t%s\n",
|
||||
*/
|
||||
#define STRING_FIELD_SIZE 256
|
||||
typedef struct _split_record
|
||||
{
|
||||
enum _enum_action {LOG_BEGIN_EDIT, LOG_ROLLBACK, LOG_COMMIT, LOG_DELETE} log_action;
|
||||
int log_action_present;
|
||||
GUID trans_guid;
|
||||
int trans_guid_present;
|
||||
GUID split_guid;
|
||||
int split_guid_present;
|
||||
Timespec log_date;
|
||||
int log_date_present;
|
||||
Timespec date_entered;
|
||||
int date_entered_present;
|
||||
Timespec date_posted;
|
||||
int date_posted_present;
|
||||
GUID acc_guid;
|
||||
int acc_guid_present;
|
||||
char acc_name[STRING_FIELD_SIZE];
|
||||
int acc_name_present;
|
||||
char trans_num[STRING_FIELD_SIZE];
|
||||
int trans_num_present;
|
||||
char trans_descr[STRING_FIELD_SIZE];
|
||||
int trans_descr_present;
|
||||
char trans_notes[STRING_FIELD_SIZE];
|
||||
int trans_notes_present;
|
||||
char split_memo[STRING_FIELD_SIZE];
|
||||
int split_memo_present;
|
||||
char split_action[STRING_FIELD_SIZE];
|
||||
int split_action_present;
|
||||
char split_reconcile;
|
||||
int split_reconcile_present;
|
||||
gnc_numeric amount;
|
||||
int amount_present;
|
||||
gnc_numeric value;
|
||||
int value_present;
|
||||
Timespec date_reconciled;
|
||||
int date_reconciled_present;
|
||||
} split_record;
|
||||
/********************************************************************\
|
||||
* gnc_file_log_replay_import
|
||||
* Entry point
|
||||
\********************************************************************/
|
||||
|
||||
SCM scm_gnc_file_log_replay ()
|
||||
{
|
||||
gnc_file_log_replay();
|
||||
return SCM_EOL;
|
||||
}
|
||||
|
||||
static char *olds;
|
||||
/* This version of strtok will only match SINGLE occurence of delim,
|
||||
returning a 0 length valid string between two consecutive ocurence of delim.
|
||||
It will also return a 0 length string instead of NULL when it reaches the end of s
|
||||
*/
|
||||
static char * my_strtok (s, delim)
|
||||
char *s;
|
||||
const char *delim;
|
||||
{
|
||||
char *token;
|
||||
/*DEBUG("strtok(): Start...");*/
|
||||
if (s == NULL)
|
||||
s = olds;
|
||||
|
||||
/* Scan leading delimiters. */
|
||||
/*s += strspn (s, delim);*/ /*Don't do it, or we will loose count.*/
|
||||
if (*s == '\0')
|
||||
{
|
||||
olds = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Find the end of the token. */
|
||||
token = s;
|
||||
s = strpbrk (token, delim);
|
||||
if (s == NULL)
|
||||
{
|
||||
/* This token finishes the string. */
|
||||
olds = strchr (token, '\0');
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Terminate the token and make OLDS point past it. */
|
||||
*s = '\0';
|
||||
olds = s + 1;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
static split_record interpret_split_record( char *record_line)
|
||||
{
|
||||
char * tok_ptr;
|
||||
split_record record;
|
||||
memset(&record,0,sizeof(record));
|
||||
DEBUG("interpret_split_record(): Start...");
|
||||
if(strlen(tok_ptr = my_strtok(record_line,"\t"))!=0)
|
||||
{
|
||||
switch(tok_ptr[0])
|
||||
{
|
||||
case 'B': record.log_action=LOG_BEGIN_EDIT;
|
||||
break;
|
||||
case 'D': record.log_action=LOG_DELETE;
|
||||
break;
|
||||
case 'C': record.log_action=LOG_COMMIT;
|
||||
break;
|
||||
case 'R': record.log_action=LOG_ROLLBACK;
|
||||
break;
|
||||
}
|
||||
record.log_action_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
string_to_guid(tok_ptr, &(record.trans_guid));
|
||||
record.trans_guid_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
string_to_guid(tok_ptr, &(record.split_guid));
|
||||
record.split_guid_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
record.log_date = gnc_iso8601_to_timespec_local(tok_ptr);
|
||||
record.log_date_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
record.date_entered = gnc_iso8601_to_timespec_local(tok_ptr);
|
||||
record.date_entered_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
record.date_posted = gnc_iso8601_to_timespec_local(tok_ptr);
|
||||
record.date_posted_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
string_to_guid(tok_ptr, &(record.acc_guid));
|
||||
record.acc_guid_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
strncpy(record.acc_name,tok_ptr,STRING_FIELD_SIZE-1);
|
||||
record.acc_name_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
strncpy(record.trans_num,tok_ptr,STRING_FIELD_SIZE-1);
|
||||
record.trans_num_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
strncpy(record.trans_descr,tok_ptr,STRING_FIELD_SIZE-1);
|
||||
record.trans_descr_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
strncpy(record.trans_notes,tok_ptr,STRING_FIELD_SIZE-1);
|
||||
record.trans_notes_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
strncpy(record.split_memo,tok_ptr,STRING_FIELD_SIZE-1);
|
||||
record.split_memo_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
strncpy(record.split_action,tok_ptr,STRING_FIELD_SIZE-1);
|
||||
record.split_action_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
record.split_reconcile = tok_ptr[0];
|
||||
record.split_reconcile_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
string_to_gnc_numeric(tok_ptr, &(record.amount));
|
||||
record.amount_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
string_to_gnc_numeric(tok_ptr, &(record.value));
|
||||
record.value_present=TRUE;
|
||||
}
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
record.date_reconciled = gnc_iso8601_to_timespec_local(tok_ptr);
|
||||
record.date_reconciled_present=TRUE;
|
||||
}
|
||||
|
||||
if(strlen(tok_ptr = my_strtok(NULL,"\t"))!=0)
|
||||
{
|
||||
PERR("interpret_split_record(): Expected number of fields exceeded!");
|
||||
}
|
||||
DEBUG("interpret_split_record(): End");
|
||||
return record;
|
||||
}
|
||||
|
||||
static void dump_split_record(split_record record)
|
||||
{
|
||||
char * string_ptr = NULL;
|
||||
char string_buf[256];
|
||||
|
||||
DEBUG("dump_split_record(): Start...");
|
||||
if(record.log_action_present)
|
||||
{
|
||||
switch(record.log_action)
|
||||
{
|
||||
case LOG_BEGIN_EDIT: DEBUG("Log action: LOG_BEGIN_EDIT");
|
||||
break;
|
||||
case LOG_DELETE: DEBUG("Log action: LOG_DELETE");
|
||||
break;
|
||||
case LOG_COMMIT: DEBUG("Log action: LOG_COMMIT");
|
||||
break;
|
||||
case LOG_ROLLBACK: DEBUG("Log action: LOG_ROLLBACK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(record.trans_guid_present)
|
||||
{
|
||||
string_ptr = guid_to_string (&(record.trans_guid));
|
||||
DEBUG("Transaction GUID: %s", string_ptr);
|
||||
g_free(string_ptr);
|
||||
}
|
||||
if(record.split_guid_present)
|
||||
{
|
||||
string_ptr = guid_to_string (&(record.split_guid));
|
||||
DEBUG("Split GUID: %s", string_ptr);
|
||||
g_free(string_ptr);
|
||||
}
|
||||
if(record.log_date_present)
|
||||
{
|
||||
gnc_timespec_to_iso8601_buff (record.log_date, string_buf);
|
||||
DEBUG("Log entry date: %s", string_buf);
|
||||
}
|
||||
if(record.date_entered_present)
|
||||
{
|
||||
gnc_timespec_to_iso8601_buff (record.date_entered, string_buf);
|
||||
DEBUG("Date entered: %s", string_buf);
|
||||
}
|
||||
if(record.date_posted_present)
|
||||
{
|
||||
gnc_timespec_to_iso8601_buff (record.date_posted, string_buf);
|
||||
DEBUG("Date posted: %s", string_buf);
|
||||
}
|
||||
if(record.acc_guid_present)
|
||||
{
|
||||
string_ptr = guid_to_string (&(record.acc_guid));
|
||||
DEBUG("Account GUID: %s", string_ptr);
|
||||
g_free(string_ptr);
|
||||
}
|
||||
if(record.acc_name_present)
|
||||
{
|
||||
DEBUG("Account name: %s", record.acc_name);
|
||||
}
|
||||
if(record.trans_num_present)
|
||||
{
|
||||
DEBUG("Transaction number: %s", record.trans_num);
|
||||
}
|
||||
if(record.trans_descr_present)
|
||||
{
|
||||
DEBUG("Transaction description: %s", record.trans_descr);
|
||||
}
|
||||
if(record.trans_notes_present)
|
||||
{
|
||||
DEBUG("Transaction notes: %s", record.trans_notes);
|
||||
}
|
||||
if(record.split_memo_present)
|
||||
{
|
||||
DEBUG("Split memo: %s", record.split_memo);
|
||||
}
|
||||
if(record.split_action_present)
|
||||
{
|
||||
DEBUG("Split action: %s", record.split_action);
|
||||
}
|
||||
if(record.split_reconcile_present)
|
||||
{
|
||||
DEBUG("Split reconcile: %c", record.split_reconcile);
|
||||
}
|
||||
if(record.amount_present)
|
||||
{
|
||||
string_ptr = gnc_numeric_to_string(record.amount);
|
||||
DEBUG("Record amount: %s", string_ptr);
|
||||
g_free(string_ptr);
|
||||
}
|
||||
if(record.value_present)
|
||||
{
|
||||
string_ptr = gnc_numeric_to_string(record.value);
|
||||
DEBUG("Record value: %s", string_ptr);
|
||||
g_free(string_ptr);
|
||||
}
|
||||
if(record.date_reconciled_present)
|
||||
{
|
||||
gnc_timespec_to_iso8601_buff (record.date_reconciled, string_buf);
|
||||
DEBUG("Reconciled date: %s", string_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* File pointer must already be at the begining of a record */
|
||||
static void process_trans_record( FILE *log_file)
|
||||
{
|
||||
char read_buf[256];
|
||||
char *read_retval;
|
||||
const char * record_end_str = "===== END";
|
||||
int first_record=TRUE;
|
||||
int record_ended = FALSE;
|
||||
int split_num = 0;
|
||||
split_record record;
|
||||
Transaction * trans = NULL;
|
||||
Split * split = NULL;
|
||||
Account * acct = NULL;
|
||||
GNCBook * book = gnc_get_current_book();
|
||||
|
||||
DEBUG("process_trans_record(): Begin...\n");
|
||||
|
||||
while( record_ended == FALSE)
|
||||
{
|
||||
read_retval = fgets(read_buf,sizeof(read_buf),log_file);
|
||||
if(read_retval!=NULL && strncmp(record_end_str,read_buf,strlen(record_end_str))!=0)/* If we are not at the end of the record */
|
||||
{
|
||||
split_num++;
|
||||
/*DEBUG("process_trans_record(): Line read: %s%s",read_buf ,"\n");*/
|
||||
record = interpret_split_record( read_buf);
|
||||
dump_split_record( record);
|
||||
if(record.log_action_present)
|
||||
{
|
||||
switch(record.log_action)
|
||||
{
|
||||
case LOG_BEGIN_EDIT: DEBUG("process_trans_record():Ignoring log action: LOG_BEGIN_EDIT"); /*Do nothing, there is no point*/
|
||||
break;
|
||||
case LOG_ROLLBACK: DEBUG("process_trans_record():Ignoring log action: LOG_ROLLBACK");/*Do nothing, since we didn't do the begin_edit either*/
|
||||
break;
|
||||
case LOG_DELETE: DEBUG("process_trans_record(): Playing back LOG_DELETE");
|
||||
if((trans=xaccTransLookup (&(record.trans_guid), book))!=NULL
|
||||
&& first_record==TRUE)
|
||||
{
|
||||
xaccTransBeginEdit(trans);
|
||||
xaccTransDestroy(trans);
|
||||
}
|
||||
else if(first_record==TRUE)
|
||||
{
|
||||
PERR("The transaction to delete was not found!");
|
||||
}
|
||||
break;
|
||||
case LOG_COMMIT: DEBUG("process_trans_record(): Playing back LOG_COMMIT");
|
||||
if(record.trans_guid_present == TRUE
|
||||
&& (trans=xaccTransLookupDirect (record.trans_guid, book)) != NULL
|
||||
&& first_record == TRUE)
|
||||
{
|
||||
DEBUG("process_trans_record(): Transaction to be edited was found");/*Destroy the current transaction, we will create a new one to replace it*/
|
||||
xaccTransBeginEdit(trans);
|
||||
xaccTransDestroy(trans);
|
||||
xaccTransCommitEdit(trans);
|
||||
}
|
||||
|
||||
if(record.trans_guid_present == TRUE
|
||||
&& first_record==TRUE)
|
||||
{
|
||||
DEBUG("process_trans_record(): Creating the new transaction");
|
||||
trans = xaccMallocTransaction (book);
|
||||
xaccTransBeginEdit(trans);
|
||||
xaccTransSetGUID (trans, &(record.trans_guid));
|
||||
/*Fill the transaction info*/
|
||||
if(record.date_entered_present)
|
||||
{
|
||||
xaccTransSetDateEnteredTS(trans,&(record.date_entered));
|
||||
}
|
||||
if(record.date_posted_present)
|
||||
{
|
||||
xaccTransSetDatePostedTS(trans,&(record.date_posted));
|
||||
}
|
||||
if(record.trans_num_present)
|
||||
{
|
||||
xaccTransSetNum(trans,record.trans_num);
|
||||
}
|
||||
if(record.trans_descr_present)
|
||||
{
|
||||
xaccTransSetDescription(trans,record.trans_descr);
|
||||
}
|
||||
if(record.trans_notes_present)
|
||||
{
|
||||
xaccTransSetNotes(trans,record.trans_notes);
|
||||
}
|
||||
}
|
||||
if(record.split_guid_present == TRUE) /*Fill the split info*/
|
||||
{
|
||||
split=xaccMallocSplit(book);
|
||||
xaccSplitSetGUID (split, &(record.split_guid));
|
||||
if(record.acc_guid_present)
|
||||
{
|
||||
acct = xaccAccountLookupDirect(record.acc_guid,book);
|
||||
xaccAccountInsertSplit(acct,split);
|
||||
}
|
||||
xaccTransAppendSplit(trans,split);
|
||||
|
||||
if(record.split_memo_present)
|
||||
{
|
||||
xaccSplitSetMemo(split,record.split_memo);
|
||||
}
|
||||
if(record.split_action_present)
|
||||
{
|
||||
xaccSplitSetAction(split,record.split_action);
|
||||
}
|
||||
if(record.date_reconciled_present)
|
||||
{
|
||||
xaccSplitSetDateReconciledTS (split, &(record.date_reconciled));
|
||||
}
|
||||
if(record.split_reconcile_present)
|
||||
{
|
||||
xaccSplitSetReconcile(split, record.split_reconcile);
|
||||
}
|
||||
|
||||
if(record.amount_present)
|
||||
{
|
||||
xaccSplitSetAmount(split, record.amount);
|
||||
}
|
||||
if(record.value_present)
|
||||
{
|
||||
xaccSplitSetValue(split, record.value);
|
||||
}
|
||||
}
|
||||
first_record=FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PERR("Corrupted record");
|
||||
}
|
||||
}
|
||||
else /* The record ended */
|
||||
{
|
||||
record_ended = TRUE;
|
||||
DEBUG("process_trans_record(): Record ended\n");
|
||||
if(trans!=NULL)/*If we played with a transaction, commit it here*/
|
||||
{
|
||||
xaccTransCommitEdit(trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gnc_file_log_replay (void)
|
||||
{
|
||||
const char *selected_filename;
|
||||
char *default_dir;
|
||||
char read_buf[256];
|
||||
char *read_retval;
|
||||
FILE *log_file;
|
||||
char * expected_header = "mod trans_guid split_guid time_now date_entered date_posted acc_guid acc_name num description notes memo action reconciled amount value date_reconciled";
|
||||
char * record_start_str = "===== START";
|
||||
|
||||
gnc_should_log(MOD_IMPORT, GNC_LOG_DEBUG);
|
||||
DEBUG("gnc_file_log_replay(): Begin...\n");
|
||||
|
||||
default_dir = gnc_lookup_string_option("__paths", "Log Files", NULL);
|
||||
if (default_dir == NULL)
|
||||
gnc_init_default_directory(&default_dir);
|
||||
selected_filename = gnc_file_dialog(_("Select a .log file to replay"),
|
||||
NULL,
|
||||
default_dir);
|
||||
|
||||
if(selected_filename!=NULL)
|
||||
{
|
||||
/* Remember the directory as the default. */
|
||||
gnc_extract_directory(&default_dir, selected_filename);
|
||||
gnc_set_string_option("__paths", "Log Files", default_dir);
|
||||
g_free(default_dir);
|
||||
|
||||
/*strncpy(file,selected_filename, 255);*/
|
||||
DEBUG("Filename found: %s",selected_filename);
|
||||
|
||||
DEBUG("Opening selected file");
|
||||
log_file = fopen(selected_filename, "r");
|
||||
if(ferror(log_file)!=0)
|
||||
{
|
||||
perror("File open failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
if((read_retval = fgets(read_buf,sizeof(read_buf),log_file)) == NULL)
|
||||
{
|
||||
DEBUG("Read error or EOF");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strncmp(expected_header,read_buf,strlen(expected_header))!=0)
|
||||
{
|
||||
PERR("File header not recognised:\n%s",read_buf);
|
||||
PERR("Expected:\n%s",expected_header);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
read_retval = fgets(read_buf,sizeof(read_buf),log_file);
|
||||
/*DEBUG("Chunk read: %s",read_retval);*/
|
||||
if(strncmp(record_start_str,read_buf,strlen(record_start_str))==0)/* If a record started */
|
||||
{
|
||||
process_trans_record(log_file);
|
||||
}
|
||||
}while(feof(log_file)==0);
|
||||
}
|
||||
}
|
||||
fclose(log_file);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** @} */
|
||||
@ -0,0 +1,34 @@
|
||||
/********************************************************************\
|
||||
* 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
/** @file
|
||||
@brief .log replay module interface
|
||||
*
|
||||
gnc-log-replay.h
|
||||
@author Copyright (c) 2003 Benoit Grégoire <bock@step.polymtl.ca>
|
||||
*/
|
||||
#ifndef OFX_IMPORT_H
|
||||
#define OFX_IMPORT_H
|
||||
|
||||
/** The gnc_file_log_replay() routine will pop up a standard file
|
||||
* selection dialogue asking the user to pick a log file to replay. If one
|
||||
* is selected the the .log file is opened and read. It's contents
|
||||
* are then silently merged in the current log file. */
|
||||
void gnc_file_log_replay (void);
|
||||
SCM scm_gnc_file_log_replay (void);
|
||||
#endif
|
||||
@ -0,0 +1,92 @@
|
||||
/********************************************************************\
|
||||
* 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
/** @addtogroup Import_Export
|
||||
@{ */
|
||||
/**@internal
|
||||
@file gncmod-log-replay.c
|
||||
@brief module definition/initialization for the log replay module
|
||||
@author Copyright (c) 2003 Benoit Grégoire bock@step.polymtl.ca
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <glib.h>
|
||||
#include <libguile.h>
|
||||
#include "guile-mappings.h"
|
||||
|
||||
#include "gnc-log-replay.h"
|
||||
#include "gnc-module.h"
|
||||
#include "gnc-module-api.h"
|
||||
|
||||
/* version of the gnc module system interface we require */
|
||||
int libgncmod_log_replay_LTX_gnc_module_system_interface = 0;
|
||||
|
||||
/* module versioning uses libtool semantics. */
|
||||
int libgncmod_log_replay_LTX_gnc_module_current = 0;
|
||||
int libgncmod_log_replay_LTX_gnc_module_revision = 0;
|
||||
int libgncmod_log_replay_LTX_gnc_module_age = 0;
|
||||
|
||||
//static GNCModule bus_core;
|
||||
//static GNCModule file;
|
||||
|
||||
/* forward references */
|
||||
char *libgncmod_log_replay_LTX_gnc_module_path(void);
|
||||
char *libgncmod_log_replay_LTX_gnc_module_description(void);
|
||||
int libgncmod_log_replay_LTX_gnc_module_init(int refcount);
|
||||
int libgncmod_log_replay_LTX_gnc_module_end(int refcount);
|
||||
|
||||
|
||||
char *
|
||||
libgncmod_log_replay_LTX_gnc_module_path(void)
|
||||
{
|
||||
return g_strdup("gnucash/import-export/log-replay");
|
||||
}
|
||||
char *
|
||||
libgncmod_log_replay_LTX_gnc_module_description(void)
|
||||
{
|
||||
return g_strdup("C code for log file replay");
|
||||
}
|
||||
int
|
||||
libgncmod_log_replay_LTX_gnc_module_init(int refcount)
|
||||
{
|
||||
if(!gnc_module_load("gnucash/engine", 0))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if(!gnc_module_load("gnucash/app-utils", 0))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if(!gnc_module_load("gnucash/gnome-utils", 0))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if(!gnc_module_load("gnucash/import-export", 0))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
scm_c_eval_string("(load-from-path \"log-replay/log-replay.scm\")");
|
||||
scm_c_define_gsubr("gnc:log-replay", 0, 0, 0, scm_gnc_file_log_replay);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
libgncmod_log_replay_LTX_gnc_module_end(int refcount)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
/** @}*/
|
||||
@ -0,0 +1,10 @@
|
||||
(define (add-log-replay-menu-item)
|
||||
(gnc:add-extension
|
||||
(gnc:make-menu-item(N_ "Replay GnuCash .log file")
|
||||
(N_ "Replay a gnucash log file after a crash. This cannot be undone.")
|
||||
(list gnc:window-name-main "File" "_Import" "")
|
||||
(lambda ()
|
||||
(gnc:log-replay)))))
|
||||
|
||||
(gnc:hook-add-dangler gnc:*ui-startup-hook* add-log-replay-menu-item)
|
||||
|
||||
Loading…
Reference in new issue