gnc-query-list.c

00001 /********************************************************************\
00002  * gnc-query-list.c -- A query display list.                        *
00003  * Copyright (C) 2003 Derek Atkins <derek@ihtfp.com>                *
00004  *                                                                  *
00005  * This program is free software; you can redistribute it and/or    *
00006  * modify it under the terms of the GNU General Public License as   *
00007  * published by the Free Software Foundation; either version 2 of   *
00008  * the License, or (at your option) any later version.              *
00009  *                                                                  *
00010  * This program is distributed in the hope that it will be useful,  *
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00013  * GNU General Public License for more details.                     *
00014  *                                                                  *
00015  * You should have received a copy of the GNU General Public License*
00016  * along with this program; if not, contact:                        *
00017  *                                                                  *
00018  * Free Software Foundation           Voice:  +1-617-542-5942       *
00019  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00020  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00021 \********************************************************************/
00022 
00023 #include "config.h"
00024 
00025 #include <gtk/gtk.h>
00026 
00027 #include "dialog-utils.h"
00028 #include "gnc-ui-util.h"
00029 #include "qof.h"
00030 #include "gnc-component-manager.h"
00031 #include "gnc-query-list.h"
00032 #include "search-param.h"
00033 #include "QueryCore.h"
00034 #include "QueryNew.h"
00035 #include "QueryObject.h"
00036 
00037 /* Signal codes */
00038 enum
00039 {
00040   LINE_TOGGLED,
00041   DOUBLE_CLICK_ENTRY,
00042   LAST_SIGNAL
00043 };
00044 
00045 typedef struct _GNCQueryListPriv  GNCQueryListPriv;
00046 
00047 struct _GNCQueryListPriv 
00048 {
00049   const QofParam * get_guid;
00050   gint            component_id;
00051 };
00052 
00053 #define GNC_QUERY_LIST_GET_PRIVATE(o)  \
00054    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_QUERY_LIST, GNCQueryListPriv))
00055 
00056 /* Impossible to get at runtime. Assume this is a reasonable number */
00057 #define ARROW_SIZE      14
00058 #define VSCROLLBAR_SLOP 40
00059 
00060 
00062 static GtkCListClass *parent_class = NULL;
00063 static guint query_list_signals[LAST_SIGNAL] = {0};
00064 
00065 
00067 static void gnc_query_list_init(GNCQueryList *list);
00068 static void gnc_query_list_init_clist (GNCQueryList *list);
00069 static void gnc_query_list_class_init(GNCQueryListClass *klass);
00070 static void gnc_query_list_select_row(GtkCList *clist, gint row,
00071                                           gint column, GdkEvent *event);
00072 static void gnc_query_list_unselect_row(GtkCList *clist, gint row,
00073                                             gint column, GdkEvent *event);
00074 static void gnc_query_list_destroy(GtkObject *object);
00075 static void gnc_query_list_fill(GNCQueryList *list);
00076 static void gnc_query_list_click_column_cb(GtkWidget *w, gint column,
00077                                                gpointer data);
00078 static void gnc_query_list_size_allocate_cb(GtkWidget *w,
00079                                                 GtkAllocation *allocation,
00080                                                 gpointer data);
00081 
00082 static void gnc_query_list_set_query_sort (GNCQueryList *list, gboolean new_column);
00083 
00084 GType
00085 gnc_query_list_get_type (void)
00086 {
00087   static GType gnc_query_list_type = 0;
00088 
00089   if (!gnc_query_list_type)
00090   {
00091     GTypeInfo type_info = {
00092       sizeof(GNCQueryListClass),        /* class_size */
00093       NULL,                             /* base_init */
00094       NULL,                             /* base_finalize */
00095       (GClassInitFunc)gnc_query_list_class_init,
00096       NULL,                             /* class_finalize */
00097       NULL,                             /* class_data */
00098       sizeof (GNCQueryList),            /* */
00099       0,                                /* n_preallocs */
00100       (GInstanceInitFunc)gnc_query_list_init,
00101     };
00102 
00103     gnc_query_list_type = g_type_register_static(GTK_TYPE_CLIST,        
00104                                                  "GNCQueryList",
00105                                                  &type_info, 0);
00106   }
00107 
00108   return gnc_query_list_type;
00109 }
00110 
00111 
00112 /********************************************************************\
00113  * gnc_query_list_new                                               *
00114  *   creates the query list                                         *
00115  *                                                                  *
00116  * Args: param_list - the list of params                            *
00117  *       query      - the query to use to find entries              *
00118  * Returns: the query list widget, or NULL if there was a problem.  *
00119 \********************************************************************/
00120 void
00121 gnc_query_list_construct (GNCQueryList *list, GList *param_list, Query *query)
00122 {
00123   GNCQueryListPriv *priv;
00124 
00125   g_return_if_fail(list);
00126   g_return_if_fail(param_list);
00127   g_return_if_fail(query);
00128   g_return_if_fail(IS_GNC_QUERY_LIST(list));
00129 
00130   /* more configuration */
00131   list->query = gncQueryCopy(query);
00132   list->column_params = param_list;
00133 
00134   /* cache the function to get the guid of this query type */
00135   priv = GNC_QUERY_LIST_GET_PRIVATE(list);
00136   priv->get_guid =
00137     qof_class_get_parameter (qof_query_get_search_for(query), QOF_PARAM_GUID);
00138 
00139   /* Initialize the CList */
00140   gnc_query_list_init_clist(list);
00141 
00142   /* Set initial sort order */
00143   gnc_query_list_set_query_sort(list, TRUE);
00144 }
00145 
00146 
00147 GtkWidget *
00148 gnc_query_list_new(GList *param_list, Query *query)
00149 {
00150   GNCQueryList *list;
00151   gint columns;
00152 
00153   g_return_val_if_fail(param_list, NULL);
00154   g_return_val_if_fail(query, NULL);
00155 
00156   columns = g_list_length(param_list);
00157   list = GNC_QUERY_LIST(g_object_new(gnc_query_list_get_type(),
00158                                      "n_columns", columns,
00159                                      NULL));
00160 
00161   gnc_query_list_construct(list, param_list, query);
00162 
00163   return GTK_WIDGET(list);
00164 }
00165 
00166 void gnc_query_list_reset_query (GNCQueryList *list, Query *query)
00167 {
00168   g_return_if_fail(list);
00169   g_return_if_fail(query);
00170   g_return_if_fail (IS_GNC_QUERY_LIST(list));
00171 
00172   gncQueryDestroy(list->query);
00173   list->query = gncQueryCopy(query);
00174   gnc_query_list_set_query_sort(list, TRUE);
00175 }
00176 
00177 static void
00178 update_booleans (GNCQueryList *list, gint row)
00179 {
00180   GtkCList *clist = GTK_CLIST(list);
00181   gpointer entry;
00182   GList *node;
00183   gint i;
00184   gboolean result;
00185 
00186   entry = gtk_clist_get_row_data (clist, row);
00187   for (i = 0, node = list->column_params; node; node = node->next, i++)
00188   {
00189     GNCSearchParam *param = node->data;
00190     const char *type = gnc_search_param_get_param_type (param);
00191 
00192     /* if this is a boolean, ignore it now -- we'll use a checkmark later */
00193     if (safe_strcmp (type, QUERYCORE_BOOLEAN))
00194       continue;
00195 
00196     result = (gboolean) GPOINTER_TO_INT(gnc_search_param_compute_value(param, entry));
00197     gnc_clist_set_check (clist, row, i, result);
00198   }
00199 }
00200 
00201 static void
00202 gnc_query_list_column_title (GNCQueryList *list, gint column, const gchar *title)
00203 {
00204   GtkWidget *hbox, *label, *arrow;
00205 
00206   hbox = gtk_hbox_new(FALSE, 2);
00207   gtk_widget_show(hbox);
00208   gtk_clist_set_column_widget(GTK_CLIST(list), column, hbox);
00209 
00210   label = gtk_label_new(title);
00211   gtk_widget_show(label);
00212   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
00213 
00214   arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_ETCHED_IN);
00215   list->title_arrows[column] = arrow;
00216   if (column == 0)
00217     gtk_widget_show(arrow);
00218   gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
00219 }
00220 
00221 static void
00222 gnc_query_list_refresh_handler (GHashTable *changes, gpointer user_data)
00223 {
00224   GNCQueryList *list = (GNCQueryList *)user_data;
00225   g_return_if_fail (list);
00226   g_return_if_fail (IS_GNC_QUERY_LIST(list));
00227 
00228   gnc_query_list_refresh (list);
00229 }
00230 
00231 static void
00232 gnc_query_list_init (GNCQueryList *list)
00233 {
00234   GNCQueryListPriv *priv;
00235 
00236   list->query = NULL;
00237   list->no_toggle = FALSE;
00238   list->always_unselect = FALSE;
00239 
00240   list->num_columns = 0;
00241   list->column_params = NULL;
00242 
00243   list->sort_column = 0;
00244   list->increasing = TRUE;
00245   list->title_arrows = NULL;
00246 
00247   list->prev_allocation = -1;
00248   list->title_widths = NULL;
00249 
00250   list->numeric_abs = FALSE;
00251   list->numeric_inv_sort = FALSE;
00252 
00253   priv = GNC_QUERY_LIST_GET_PRIVATE(list);
00254   priv->component_id =
00255     gnc_register_gui_component ("gnc-query-list-cm-class",
00256                                 gnc_query_list_refresh_handler,
00257                                 NULL, list);
00258 }
00259 
00260 static void
00261 gnc_query_list_init_clist (GNCQueryList *list)
00262 {
00263   GtkCList *clist = GTK_CLIST (list);
00264   GtkStyle *style;
00265   GList *node;
00266   gchar **titles;
00267   gint i;
00268 
00269   /* compute the number of columns and fill in the rest of the list */
00270   list->num_columns = g_list_length(list->column_params);
00271   list->title_arrows = g_new0(GtkWidget*, list->num_columns);
00272   list->title_widths = g_new0(gint, list->num_columns);
00273 
00274   /* build an array of titles */
00275   titles = g_new0(gchar*, list->num_columns);
00276   for (i = 0, node = list->column_params; node; node = node->next, i++) {
00277     GNCSearchParam *param = node->data;
00278     titles[i] = (gchar *)param->title;
00279   }
00280 
00281   gtk_clist_column_titles_show (clist);
00282   gtk_clist_set_shadow_type (clist, GTK_SHADOW_IN);
00283 
00284   /* build all the column titles */
00285   for (i = 0; i < list->num_columns; i++)
00286     gnc_query_list_column_title(list, i, titles[i]);
00287 
00288   /* set the column justification */
00289   for (i = 0, node = list->column_params; node; node = node->next, i++) {
00290     GNCSearchParam *param = node->data;
00291     gtk_clist_set_column_justification (clist, i, param->justify);
00292 
00293     if (param->passive)
00294       gtk_clist_column_title_passive (clist, i);
00295 
00296     if (param->non_resizeable)
00297       gtk_clist_set_column_resizeable (clist, i, FALSE);
00298   }
00299 
00300   g_signal_connect (clist, "click_column",
00301                     G_CALLBACK(gnc_query_list_click_column_cb),
00302                     NULL);
00303   g_signal_connect (clist, "size_allocate",
00304                     G_CALLBACK(gnc_query_list_size_allocate_cb),
00305                     NULL);
00306 
00307   style = gtk_widget_get_style (GTK_WIDGET(list));
00308 
00309 #if 0
00310   {
00311     GdkFont *font = NULL;
00312     gint width;
00313 
00314     font = style->font;
00315     if (font != NULL)
00316     {
00317       for (i = 0, node = list->column_params; node; node = node->next, i++)
00318       {
00319         GNCSearchParam *param = node->data;
00320         width = gdk_string_width (font, titles[i]) + 5;
00321         if (!param->passive)
00322           width += ARROW_SIZE;
00323         gtk_clist_set_column_min_width (clist, i, width);
00324         list->title_widths[i] = width;
00325       }
00326     }
00327   }
00328 #endif
00329   g_free(titles);
00330 }
00331 
00332 static void
00333 gnc_query_list_class_init (GNCQueryListClass *klass)
00334 {
00335   GtkObjectClass    *object_class;
00336   GtkWidgetClass    *widget_class;
00337   GtkContainerClass *container_class;
00338   GtkCListClass     *clist_class;
00339 
00340   object_class =    (GtkObjectClass*) klass;
00341   widget_class =    (GtkWidgetClass*) klass;
00342   container_class = (GtkContainerClass*) klass;
00343   clist_class =     (GtkCListClass*) klass;
00344 
00345   parent_class = gtk_type_class(GTK_TYPE_CLIST);
00346 
00347   g_type_class_add_private(klass, sizeof(GNCQueryListPriv));
00348 
00349   query_list_signals[LINE_TOGGLED] =
00350     g_signal_new("line_toggled",
00351                  G_TYPE_FROM_CLASS (object_class),
00352                  G_SIGNAL_RUN_FIRST,
00353                  G_STRUCT_OFFSET(GNCQueryListClass, line_toggled),
00354                  NULL, NULL,
00355                  g_cclosure_marshal_VOID__POINTER,
00356                  G_TYPE_NONE,
00357                  1,
00358                  G_TYPE_POINTER);
00359 
00360   query_list_signals[DOUBLE_CLICK_ENTRY] =
00361     g_signal_new("double_click_entry",
00362                  G_TYPE_FROM_CLASS (object_class),
00363                  G_SIGNAL_RUN_FIRST,
00364                  G_STRUCT_OFFSET(GNCQueryListClass, double_click_entry),
00365                  NULL, NULL,
00366                  g_cclosure_marshal_VOID__POINTER,
00367                  G_TYPE_NONE,
00368                  1,
00369                  G_TYPE_POINTER);
00370 
00371   object_class->destroy = gnc_query_list_destroy;
00372 
00373   clist_class->select_row = gnc_query_list_select_row;
00374   clist_class->unselect_row = gnc_query_list_unselect_row;
00375 
00376   klass->line_toggled = NULL;
00377   klass->double_click_entry = NULL;
00378 }
00379 
00380 static void
00381 gnc_query_list_toggle (GNCQueryList *list)
00382 {
00383   gpointer entry;
00384   gint row;
00385 
00386   g_return_if_fail (IS_GNC_QUERY_LIST(list));
00387 
00388   if (list->no_toggle)
00389     return;
00390 
00391   row = list->current_row;
00392   entry = gtk_clist_get_row_data (GTK_CLIST(list), row);
00393   list->current_entry = entry;
00394 
00395   g_signal_emit (list, query_list_signals[LINE_TOGGLED], 0, entry);
00396 
00397   update_booleans (list, row);
00398 }
00399 
00400 static void
00401 gnc_query_list_select_row (GtkCList *clist, gint row, gint column,
00402                                GdkEvent *event)
00403 {
00404   GNCQueryList *list = GNC_QUERY_LIST(clist);
00405 
00406   list->current_row = row;
00407 
00408   gnc_query_list_toggle (list);
00409   if (event == NULL) {
00410     /* User pressed the space key */
00411     parent_class->scroll_vertical(clist, GTK_SCROLL_STEP_FORWARD, 0.0);
00412   }
00413 
00414   /* This will trigger an unselect event for the currently selected row */
00415   parent_class->select_row (clist, row, column, event);
00416 
00417   if (event && (event->type == GDK_2BUTTON_PRESS))
00418   {
00419     gpointer entry;
00420 
00421     entry = gtk_clist_get_row_data (clist, row);
00422 
00423     g_signal_emit(list, query_list_signals[DOUBLE_CLICK_ENTRY], 0, entry);
00424   }
00425 }
00426 
00427 static void
00428 gnc_query_list_unselect_row (GtkCList *clist, gint row, gint column,
00429                                  GdkEvent *event)
00430 {
00431   GNCQueryList *list = GNC_QUERY_LIST(clist);
00432 
00433   if (row == list->current_row)
00434   {
00435     gnc_query_list_toggle (list);
00436     if (event == NULL) {
00437       /* User pressed the space key */
00438       parent_class->scroll_vertical(clist, GTK_SCROLL_STEP_FORWARD, 0.0);
00439     }
00440     if (!list->always_unselect)
00441       return;
00442   }
00443 
00444   parent_class->unselect_row (clist, row, column, event);
00445 
00446   if (event && (event->type == GDK_2BUTTON_PRESS))
00447   {
00448     gpointer entry;
00449 
00450     entry = gtk_clist_get_row_data (clist, row);
00451 
00452     g_signal_emit (list, query_list_signals[DOUBLE_CLICK_ENTRY], 0, entry);
00453   }
00454 }
00455 
00456 static void
00457 gnc_query_list_destroy (GtkObject *object)
00458 {
00459   GNCQueryList *list = GNC_QUERY_LIST(object);
00460   GNCQueryListPriv *priv;
00461 
00462   priv = GNC_QUERY_LIST_GET_PRIVATE(list);
00463   if (priv->component_id > 0) {
00464     gnc_unregister_gui_component (priv->component_id);
00465     priv->component_id = 0;
00466   }
00467   if (list->query)
00468   {
00469     xaccFreeQuery(list->query);
00470     list->query = NULL;
00471   }
00472   if (list->column_params)
00473   {
00474     /* XXX: free the params list??? */
00475   }
00476   if (list->title_arrows)
00477   {
00478     g_free(list->title_arrows);
00479     list->title_arrows = NULL;
00480   }
00481   if (list->title_widths)
00482   {
00483     g_free(list->title_widths);
00484     list->title_widths = NULL;
00485   }
00486 
00487   if (GTK_OBJECT_CLASS(parent_class)->destroy)
00488     GTK_OBJECT_CLASS(parent_class)->destroy (object);
00489 }
00490 
00491 gint
00492 gnc_query_list_get_needed_height (GNCQueryList *list, gint num_rows)
00493 {
00494   GtkCList *clist;
00495   gint list_height;
00496   gint title_height;
00497 
00498   g_return_val_if_fail (list != NULL, 0);
00499   g_return_val_if_fail (IS_GNC_QUERY_LIST(list), 0);
00500 
00501   if (!GTK_WIDGET_REALIZED (list))
00502     return 0;
00503 
00504   clist = GTK_CLIST (list);
00505 
00506   /* sync with gtkclist.c */
00507   title_height = (clist->column_title_area.height +
00508                   (GTK_WIDGET(list)->style->ythickness +
00509                    GTK_CONTAINER(list)->border_width) * 2);
00510   list_height = (clist->row_height * num_rows) + (num_rows + 1);
00511 
00512   return title_height + list_height;
00513 }
00514 
00515 gint
00516 gnc_query_list_get_num_entries (GNCQueryList *list)
00517 {
00518   g_return_val_if_fail (list != NULL, 0);
00519   g_return_val_if_fail (IS_GNC_QUERY_LIST(list), 0);
00520 
00521   return list->num_entries;
00522 }
00523 
00524 gpointer
00525 gnc_query_list_get_current_entry (GNCQueryList *list)
00526 {
00527   g_return_val_if_fail (list != NULL, NULL);
00528   g_return_val_if_fail (IS_GNC_QUERY_LIST(list), NULL);
00529 
00530   return list->current_entry;
00531 }
00532 
00533 /********************************************************************\
00534  * gnc_query_list_recompute_widths                              *
00535  *   Given a new widget width, recompute the widths of each column. *
00536  *   Give any excess allocation to the description field. This also * 
00537  *   handles the case of allocating column widths when the list is  *
00538  *   first filled with data.                                        *
00539  *                                                                  *
00540  * Args: list - a GncQueryList widget                           *
00541  *       allocated - the allocated width for this list              *
00542  * Returns: nothing                                                 *
00543 \********************************************************************/
00544 static void
00545 gnc_query_list_recompute_widths (GNCQueryList *list, gint allocated)
00546 {
00547   GtkCList *clist = GTK_CLIST(list);
00548   gint total_width, desc_width = 0, excess, i;
00549 
00550   /* Prevent loops when allocation is bigger than total widths */
00551   if (allocated == list->prev_allocation)
00552     return;
00553 
00554   /* Enforce column minimum widths */
00555   total_width = 0;
00556   for (i = 0; i < list->num_columns; i++)
00557   {
00558     gint width;
00559 
00560     width = gtk_clist_optimal_column_width(clist, i);
00561     if (width < list->title_widths[i])
00562       width = list->title_widths[i];
00563     total_width += width;
00564     gtk_clist_set_column_width (clist, i, width);
00565     if (i == 2)
00566       desc_width = width;
00567   }
00568 
00569   /* Did the list use its full allocation? 
00570    *
00571    * Add/subtract any underage/overage to/from the description column
00572    */
00573   if (allocated <= 1)
00574     allocated = list->prev_allocation;
00575   list->prev_allocation = allocated;
00576   excess = allocated - total_width - VSCROLLBAR_SLOP;
00577 
00578   /* XXX: Choose a generic column to resize */
00579   gtk_clist_set_column_width (clist, 2, desc_width + excess);
00580 }
00581 
00582 /********************************************************************\
00583  * gnc_query_list_size_allocate_cb                              *
00584  *   The allocated size has changed. Need to recompute the          *
00585  *   column widths                                                  * 
00586  *                                                                  *
00587  * Args: w - a GncQueryList widget                              *
00588  *       allocation - a widget allocation desctiption               *
00589  *       data - unused                                              *
00590  * Returns: nothing                                                 *
00591 \********************************************************************/
00592 static void
00593 gnc_query_list_size_allocate_cb (GtkWidget *w,
00594                                  GtkAllocation *allocation,
00595                                  gpointer data)
00596 {
00597   GNCQueryList *list = GNC_QUERY_LIST(w);
00598 
00599   g_return_if_fail (list != NULL);
00600   gnc_query_list_recompute_widths(list, allocation->width);
00601 }
00602 
00603 /********************************************************************\
00604  * gnc_query_list_refresh                                       *
00605  *   refreshes the list                                             *
00606  *                                                                  *
00607  * Args: list - list to refresh                                     *
00608  * Returns: nothing                                                 *
00609 \********************************************************************/
00610 void
00611 gnc_query_list_refresh (GNCQueryList *list)
00612 {
00613   GtkCList *clist = GTK_CLIST(list);
00614   GtkAdjustment *adjustment;
00615   gfloat save_value = 0.0;
00616   gpointer *old_focus_entry;
00617   gpointer *old_entry;
00618   gint old_focus_row;
00619   gint new_row;
00620 
00621   g_return_if_fail (list != NULL);
00622   g_return_if_fail (IS_GNC_QUERY_LIST(list));
00623 
00624   adjustment = gtk_clist_get_vadjustment (GTK_CLIST(list));
00625   if (adjustment != NULL)
00626     save_value = adjustment->value;
00627 
00628   old_focus_row = clist->focus_row;
00629   old_focus_entry = gtk_clist_get_row_data (clist, old_focus_row);
00630 
00631   gtk_clist_freeze (clist);
00632   gtk_clist_clear (clist);
00633 
00634   old_entry = list->current_entry;
00635   list->num_entries = 0;
00636   list->current_row = -1;
00637   list->current_entry = NULL;
00638 
00639   gnc_query_list_fill (list);
00640 
00641   gnc_query_list_recompute_widths (list, -1);
00642 
00643   if (adjustment)
00644   {
00645     save_value = CLAMP (save_value, adjustment->lower, adjustment->upper);
00646     gtk_adjustment_set_value (adjustment, save_value);
00647   }
00648 
00649   if (old_entry)
00650   {
00651     new_row = gtk_clist_find_row_from_data (clist, old_entry);
00652     if (new_row >= 0)
00653     {
00654       list->no_toggle = TRUE;
00655       gtk_clist_select_row (clist, new_row, 0);
00656       list->no_toggle = FALSE;
00657       list->current_entry = old_entry;
00658     }
00659   }
00660 
00661   if (old_focus_entry)
00662   {
00663     new_row = gtk_clist_find_row_from_data (clist, old_focus_entry);
00664 
00665     if (new_row < 0)
00666       new_row = old_focus_row;
00667 
00668     if (new_row >= 0)
00669       clist->focus_row = new_row;
00670   }
00671 
00672   gtk_clist_thaw (clist);
00673 }
00674 
00675 /********************************************************************\
00676  * gnc_query_list_set_query_sort                                    *
00677  *   sets the sorting order of entries in the list                  *
00678  *                                                                  *
00679  * Args: list       - list to change the sort order for             *
00680  *       new_column - is this a new column (so should we set the    *
00681  *                    query sort order or just set the 'increasing' *
00682  * Returns: nothing                                                 *
00683 \********************************************************************/
00684 static void
00685 gnc_query_list_set_query_sort (GNCQueryList *list, gboolean new_column)
00686 {
00687   gboolean sort_order = list->increasing;
00688   GList *node;
00689   GNCSearchParam *param;
00690 
00691   /* Find the column parameter definition */
00692   node = g_list_nth(list->column_params, list->sort_column);
00693   param = node->data;
00694 
00695   /* If we're asked to invert numerics, and if this is a numeric or
00696    * debred column, then invert the sort order.
00697    */
00698   if (list->numeric_inv_sort) {
00699     const char *type = gnc_search_param_get_param_type (param);
00700     if (!safe_strcmp(type, QUERYCORE_NUMERIC) ||
00701         !safe_strcmp(type, QUERYCORE_DEBCRED))
00702       sort_order = !sort_order;
00703   }
00704 
00705   /* Set the sort order for the engine, if the key changed */
00706   if (new_column)
00707   {
00708     GSList *p1, *p2;
00709 
00710     p1 = gnc_search_param_get_param_path(param);
00711     p2 = g_slist_prepend(NULL, QUERY_DEFAULT_SORT);
00712     gncQuerySetSortOrder (list->query, p1, p2, NULL);
00713   }
00714 
00715   xaccQuerySetSortIncreasing (list->query,
00716                               sort_order,
00717                               sort_order,
00718                               sort_order);
00719 
00720   /*
00721    * Recompute the list. Is this really necessary? Why not just sort
00722    * the rows already in the clist?  Answer: it would be an n-squared
00723    * algorithm to get the clist to match the resulting list.
00724    */
00725   gnc_query_list_refresh(list);
00726 }
00727 
00728 /********************************************************************\
00729  * gnc_query_list_set_sort_column                                   *
00730  *   sets the sorting order of entries in the list                  *
00731  *                                                                  *
00732  * Args: list   - list to change the sort order for                 *
00733  *       column - the column to sort on                             *
00734  * Returns: nothing                                                 *
00735 \********************************************************************/
00736 static void
00737 gnc_query_list_set_sort_column (GNCQueryList *list, gint sort_column)
00738 {
00739   gint column;
00740   gboolean new_column = FALSE;
00741 
00742   g_return_if_fail (list != NULL);
00743   g_return_if_fail (IS_GNC_QUERY_LIST(list));
00744   g_return_if_fail (list->query != NULL);
00745 
00746   /* Clear all arrows */
00747   for (column = 0; column < list->num_columns; column++)
00748   {
00749     if (list->title_arrows[column])
00750       gtk_widget_hide(list->title_arrows[column]);
00751   }
00752 
00753   /* Is this a new column or a re-click on the existing column? */
00754   column = sort_column;
00755   new_column = (list->sort_column != sort_column);
00756 
00757   list->increasing = new_column ? TRUE : !list->increasing;
00758   list->sort_column = sort_column;
00759 
00760   /* Set the appropriate arrow */
00761   gtk_arrow_set(GTK_ARROW(list->title_arrows[column]),
00762                 list->increasing ? GTK_ARROW_DOWN : GTK_ARROW_UP,
00763                 GTK_SHADOW_ETCHED_IN);
00764   gtk_widget_show(list->title_arrows[column]);
00765 
00766   gnc_query_list_set_query_sort (list, new_column);
00767 }
00768 
00769 static void
00770 gnc_query_list_click_column_cb(GtkWidget *w, gint column, gpointer data)
00771 {
00772   GNCQueryList *list = GNC_QUERY_LIST(w);
00773   gnc_query_list_set_sort_column(list, column);
00774 }
00775 
00776 static void
00777 gnc_query_list_fill(GNCQueryList *list)
00778 {
00779   GNCQueryListPriv *priv;
00780   gchar *strings[list->num_columns + 1];
00781   GList *entries, *item;
00782   const GUID *guid;
00783   gint i;
00784 
00785   /* Clear all watches */
00786   priv = GNC_QUERY_LIST_GET_PRIVATE(list);
00787   gnc_gui_component_clear_watches (priv->component_id);
00788 
00789   /* Reverse the list now because 'append()' takes too long */
00790   entries = gncQueryRun(list->query);
00791   
00792   for (item = entries; item; item = item->next)
00793   {
00794     GList *node;
00795     gint row;
00796     const QofParam *gup;
00797     QofParam *qp= NULL;
00798 
00799     for (i = 0, node = list->column_params; node; node = node->next)
00800     {
00801       GNCSearchParam *param = node->data;
00802       GSList *converters = gnc_search_param_get_converters (param);
00803       const char *type = gnc_search_param_get_param_type (param);
00804       gpointer res = item->data;
00805 
00806       /* if this is a boolean, ignore it now -- we'll use a checkmark later */
00807       if (!safe_strcmp (type, QUERYCORE_BOOLEAN)) {
00808         strings[i++] = g_strdup("");
00809         continue;
00810       }
00811 
00812       /* Do all the object conversions */
00813       for (; converters; converters = converters->next) 
00814       {
00815          qp = converters->data;
00816          if (converters->next)
00817          {
00818             res = (qp->param_getfcn)(res, qp);
00819          }
00820       }
00821 
00822       /* Now convert this to a text value for the row */
00823       if (!safe_strcmp(type, QUERYCORE_DEBCRED) ||
00824           !safe_strcmp(type, QUERYCORE_NUMERIC))
00825       {
00826         gnc_numeric (*nfcn)(gpointer, QofParam *) = 
00827                 (gnc_numeric(*)(gpointer, QofParam *))(qp->param_getfcn);
00828         gnc_numeric value = nfcn(res, qp);
00829         if (list->numeric_abs)
00830           value = gnc_numeric_abs (value);
00831         strings[i++] = g_strdup(xaccPrintAmount(value,gnc_default_print_info(FALSE)));
00832       } else
00833         strings[i++] = gncQueryCoreToString (type, res, qp);
00834     }
00835 
00836     row = gtk_clist_append (GTK_CLIST(list), (gchar **) strings);
00837     gtk_clist_set_row_data (GTK_CLIST(list), row, item->data);
00838 
00839     /* Free up our strings */
00840     for (i = 0; i < list->num_columns; i++) {
00841       if (strings[i])
00842         g_free (strings[i]);
00843     }
00844 
00845     /* Now update any checkmarks */
00846     update_booleans (list, row);
00847 
00848     /* and set a watcher on this item */
00849     gup = priv->get_guid;
00850     guid = (const GUID*)((gup->param_getfcn)(item->data, gup));
00851     gnc_gui_component_watch_entity (priv->component_id, guid,
00852                                     QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
00853 
00854     list->num_entries++;
00855   }
00856 }
00857 
00858 /********************************************************************\
00859  * gnc_query_list_unselect_all                                      *
00860  *   unselect all items in the list                                 *
00861  *                                                                  *
00862  * Args: list - list to unselect all                                *
00863  * Returns: nothing                                                 *
00864 \********************************************************************/
00865 void
00866 gnc_query_list_unselect_all(GNCQueryList *list)
00867 {
00868   g_return_if_fail (list != NULL);
00869   g_return_if_fail (IS_GNC_QUERY_LIST(list));
00870 
00871   list->no_toggle = TRUE;
00872   list->always_unselect = TRUE;
00873 
00874   gtk_clist_unselect_all (GTK_CLIST(list));
00875 
00876   list->always_unselect = FALSE;
00877   list->no_toggle = FALSE;
00878 
00879   list->current_entry = NULL;
00880 }
00881 
00882 
00883 gboolean gnc_query_list_item_in_list (GNCQueryList *list, gpointer item)
00884 {
00885   g_return_val_if_fail(list, FALSE);
00886   g_return_val_if_fail(item, FALSE);
00887   g_return_val_if_fail(IS_GNC_QUERY_LIST(list), FALSE);
00888 
00889   return (gtk_clist_find_row_from_data(GTK_CLIST(list), item) != -1);
00890 }
00891 
00892 void gnc_query_list_refresh_item (GNCQueryList *list, gpointer item)
00893 {
00894   gint row;
00895 
00896   g_return_if_fail(list);
00897   g_return_if_fail(item);
00898   g_return_if_fail(IS_GNC_QUERY_LIST(list));
00899 
00900   row = gtk_clist_find_row_from_data(GTK_CLIST(list), item);
00901   if (row != -1)
00902     update_booleans (list, row);
00903 }
00904 
00905 void
00906 gnc_query_list_set_numerics (GNCQueryList *list, gboolean abs, gboolean inv_sort)
00907 {
00908   g_return_if_fail(list);
00909   g_return_if_fail(IS_GNC_QUERY_LIST(list));
00910 
00911   list->numeric_abs = abs;
00912   list->numeric_inv_sort = inv_sort;
00913 }

Generated on Tue Oct 14 05:04:45 2008 for GnuCash by  doxygen 1.5.2