diff --git a/ChangeLog b/ChangeLog index e207cb8df2..be1e812535 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-05-12 Derek Atkins + + * src/gnome-utils/gnc-query-list.[ch]: an abstracted Query list + widget to display the results of a Query. + 2003-05-11 David Hampton * src/gnome/dialog-commodities.c: diff --git a/src/gnome-utils/gnc-query-list.c b/src/gnome-utils/gnc-query-list.c new file mode 100644 index 0000000000..65bb2800b3 --- /dev/null +++ b/src/gnome-utils/gnc-query-list.c @@ -0,0 +1,773 @@ +/********************************************************************\ + * gnc-query-list.c -- A query display list. * + * Copyright (C) 2003 Derek Atkins * + * * + * 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 + +#include "dialog-utils.h" +#include "gnc-ui-util.h" +#include "gnc-engine-util.h" +#include "messages.h" +#include "gnc-query-list.h" +#include "search-param.h" + +/* Signal codes */ +enum +{ + LINE_TOGGLED, + DOUBLE_CLICK_ENTRY, + LAST_SIGNAL +}; + + +/* 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. * +\********************************************************************/ +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())); + + /* more configuration */ + list->query = gncQueryCopy(query); + list->column_params = param_list; + + /* Initialize the CList */ + gnc_query_list_init_clist(list); + + 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_init (GNCQueryList *list) +{ + list->query = NULL; + list->no_toggle = 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; +} + + +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); + } + /* XXX + 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->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; + + /* XXX: Maybe invert the sort order? + if ((list->list_type == RECLIST_CREDIT) && (key == BY_AMOUNT)) + 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; + gint i; + + /* Reverse the list now because 'append()' takes too long */ + entries = gncQueryRun(list->query); + entries = g_list_reverse(entries); + + 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); + strings[i++] = g_strdup(xaccPrintAmount(value,gnc_default_print_info(FALSE))); + } else + strings[i++] = gncQueryCoreToString (type, res, fcn); + } + + row = gtk_clist_prepend (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); + + list->num_entries++; + } + + /* Reverse the list again for the query code */ + g_list_reverse(entries); +} diff --git a/src/gnome-utils/gnc-query-list.h b/src/gnome-utils/gnc-query-list.h new file mode 100644 index 0000000000..fd67cd9fa5 --- /dev/null +++ b/src/gnome-utils/gnc-query-list.h @@ -0,0 +1,102 @@ +/********************************************************************\ + * gnc-query-list.h -- GnuCash GNOME query display list widget * + * Copyright (C) 2003 Derek Atkins * + * * + * 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 * +\********************************************************************/ + +#ifndef GNC_QUERY_LIST_H +#define GNC_QUERY_LIST_H + +#include + +#include "Query.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GTK_TYPE_GNC_QUERY_LIST (gnc_query_list_get_type ()) +#define GNC_QUERY_LIST(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_GNC_QUERY_LIST, GNCQueryList)) +#define GNC_QUERY_LIST_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_GNC_QUERY_LIST, GNCQueryListClass)) +#define IS_GNC_QUERY_LIST(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_GNC_QUERY_LIST)) +#define IS_GNC_QUERY_LIST_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_GNC_QUERY_LIST)) + +typedef struct _GNCQueryList GNCQueryList; +typedef struct _GNCQueryListClass GNCQueryListClass; + +struct _GNCQueryList +{ + GtkCList clist; + + /* Query information */ + Query *query; + gboolean no_toggle; + gint current_row; + gint num_entries; + gpointer current_entry; + + /* Column information */ + gint num_columns; + GList *column_params; + + /* Sorting info */ + gint sort_column; + gboolean increasing; + GtkWidget **title_arrows; + + /* Column resizing */ + gint prev_allocation; + gint *title_widths; +}; + +struct _GNCQueryListClass +{ + GtkCListClass parent_class; + + void (*line_toggled) (GNCQueryList *list, gpointer entry); + void (*double_click_entry) (GNCQueryList *list, gpointer entry); +}; + +/*********************************************************** + * public functions * + ***********************************************************/ + +GtkType gnc_query_list_get_type (void); + +/* The param_list remains owned by the caller but is used by the + * query-list; do not destroy it until you destroy this query-list. + * The query will be copied by the query-list so the caller may do + * whatever they want. + */ +GtkWidget * gnc_query_list_new (GList *param_list, Query *query); +void gnc_query_list_reset_query (GNCQueryList *list, Query *query); + +gint gnc_query_list_get_needed_height(GNCQueryList *list, gint num_rows); + +gint gnc_query_list_get_num_entries(GNCQueryList *list); + +gpointer gnc_query_list_get_current_entry(GNCQueryList *list); + +void gnc_query_list_refresh (GNCQueryList *list); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* GNC_QUERY_LIST_H */