GnuCash  5.6-150-g038405b370+
gnc-query-view.c
1 /********************************************************************\
2  * gnc-query-view.c -- A query display view. *
3  * Copyright (C) 2003 Derek Atkins <derek@ihtfp.com> *
4  * Copyright (C) 2012 Robert Fewell *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22 \********************************************************************/
23 
24 #include <config.h>
25 
26 #include <gtk/gtk.h>
27 
28 #include "dialog-utils.h"
29 #include "gnc-ui-util.h"
30 #include "qof.h"
31 #include "gnc-component-manager.h"
32 #include "gnc-query-view.h"
33 #include "search-param.h"
34 
35 /* This static indicates the debugging module that this .o belongs to. */
36 static QofLogModule log_module = GNC_MOD_GUI;
37 
38 /* Signal codes */
39 enum
40 {
41  COLUMN_TOGGLED,
42  ROW_SELECTED,
43  DOUBLE_CLICK_ENTRY,
44  LAST_SIGNAL
45 };
46 
47 typedef struct _GNCQueryViewPrivate GNCQueryViewPrivate;
48 
50 {
51  const QofParam *get_guid;
52  gint component_id;
53 };
54 
55 G_DEFINE_TYPE_WITH_PRIVATE(GNCQueryView, gnc_query_view, GTK_TYPE_TREE_VIEW)
56 
57 #define GNC_QUERY_VIEW_GET_PRIVATE(o) \
58  ((GNCQueryViewPrivate*)gnc_query_view_get_instance_private((GNCQueryView*)o))
59 
61 static guint query_view_signals[LAST_SIGNAL] = {0};
62 
64 static void gnc_query_view_init_view (GNCQueryView *qview);
65 static void gnc_query_view_select_row_cb (GtkTreeSelection *selection,
66  gpointer user_data);
67 static void gnc_query_view_toggled_cb (GtkCellRendererToggle *cell_renderer,
68  gchar *path, gpointer user_data);
69 static void gnc_query_view_double_click_cb (GtkTreeView *tree_view,
70  GtkTreePath *path,
71  GtkTreeViewColumn *column,
72  gpointer user_data);
73 
74 static void gnc_query_view_destroy (GtkWidget *widget);
75 static void gnc_query_view_fill (GNCQueryView *qview);
76 static void gnc_query_view_set_query_sort (GNCQueryView *qview,
77  gboolean new_column);
78 
79 
80 /********************************************************************\
81  * gnc_query_view_new *
82  * creates the query view *
83  * *
84  * Args: param_list - the list of params *
85  * query - the query to use to find entries *
86  * Returns: the query view widget, or NULL if there was a problem. *
87 \********************************************************************/
88 void
89 gnc_query_view_construct (GNCQueryView *qview, GList *param_list, Query *query)
90 {
91  GNCQueryViewPrivate *priv;
92 
93  g_return_if_fail (qview);
94  g_return_if_fail (param_list);
95  g_return_if_fail (query);
96  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
97 
98  /* more configuration */
99  qview->query = qof_query_copy (query);
100  qview->column_params = param_list;
101 
102  /* cache the function to get the guid of this query type */
103  priv = GNC_QUERY_VIEW_GET_PRIVATE(qview);
104  priv->get_guid = qof_class_get_parameter (qof_query_get_search_for (query),
105  QOF_PARAM_GUID);
106 
107  /* Initialize the Tree View */
108  gnc_query_view_init_view (qview);
109 
110  /* Set initial sort order */
111  gnc_query_view_set_query_sort (qview, TRUE);
112 }
113 
114 GtkWidget *
115 gnc_query_view_new (GList *param_list, Query *query)
116 {
117  GNCQueryView *qview;
118  GtkListStore *liststore;
119  GList *node;
120  gint columns, i;
121  gsize array_size;
122  GType *types;
123 
124  g_return_val_if_fail (param_list, NULL);
125  g_return_val_if_fail (query, NULL);
126 
127  /* Add 1 to param_list length for extra pointer column */
128  columns = g_list_length (param_list) + 1;
129  qview = GNC_QUERY_VIEW(g_object_new (gnc_query_view_get_type (), NULL));
130 
131  array_size = sizeof(GType) * columns;
132  types = g_slice_alloc (array_size);
133 
134  types[0] = G_TYPE_POINTER;
135 
136  /* Get the types for the list store */
137  for (i = 0, node = param_list; node; node = node->next, i++)
138  {
139  GNCSearchParamSimple *param = node->data;
140  const char *type;
141 
142  g_assert (GNC_IS_SEARCH_PARAM_SIMPLE(param));
143 
144  type = gnc_search_param_get_param_type ((GNCSearchParam *) param);
145 
146  if (g_strcmp0 (type, QOF_TYPE_BOOLEAN) == 0)
147  types[i+1] = G_TYPE_BOOLEAN;
148  else
149  types[i+1] = G_TYPE_STRING;
150  }
151 
152  /* Create the list store and add to treeview */
153  liststore = gtk_list_store_newv (columns, types );
154  gtk_tree_view_set_model (GTK_TREE_VIEW(qview), GTK_TREE_MODEL(liststore));
155  g_object_unref (liststore);
156 
157  /* Free array */
158  g_slice_free1 (array_size, types);
159 
160  gnc_query_view_construct (qview, param_list, query);
161 
162  return GTK_WIDGET(qview);
163 }
164 
165 void gnc_query_view_reset_query (GNCQueryView *qview, Query *query)
166 {
167  g_return_if_fail (qview);
168  g_return_if_fail (query);
169  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
170 
171  qof_query_destroy (qview->query);
172  qview->query = qof_query_copy (query);
173 
174  gnc_query_view_set_query_sort (qview, TRUE);
175 }
176 
177 static void
178 gnc_query_view_refresh_handler (GHashTable *changes, gpointer user_data)
179 {
180  GNCQueryView *qview = (GNCQueryView *)user_data;
181  g_return_if_fail (qview);
182  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
183 
184  gnc_query_view_set_query_sort (qview, TRUE);
185 }
186 
187 static void
188 gnc_query_view_init (GNCQueryView *qview)
189 {
190  GNCQueryViewPrivate *priv;
191 
192  // Set the name for this widget so it can be easily manipulated with css
193  gtk_widget_set_name (GTK_WIDGET(qview), "gnc-id-query-view-view");
194 
195  qview->query = NULL;
196 
197  qview->num_columns = 0;
198  qview->column_params = NULL;
199 
200  qview->use_scroll_to_selection = FALSE;
201 
202  qview->sort_column = 0;
203  qview->increasing = FALSE;
204 
205  qview->numeric_abs = FALSE;
206  qview->numeric_inv_sort = FALSE;
207 
208  priv = GNC_QUERY_VIEW_GET_PRIVATE(qview);
209  priv->component_id = gnc_register_gui_component ("gnc-query-view-cm-class",
210  gnc_query_view_refresh_handler,
211  NULL, qview);
212 }
213 
214 static gint
215 sort_iter_compare_func (GtkTreeModel *model,
216  GtkTreeIter *a,
217  GtkTreeIter *b,
218  gpointer userdata)
219 {
220  /* This is really a dummy sort function, it leaves the list as is. */
221  return 0;
222 }
223 
224 /********************************************************************\
225  * gnc_query_sort_order *
226  * allows the sort order to be specified *
227  * *
228  * Args: qview - the view to sort *
229  * column - the sort column in the tree view, 1 -> *
230  * order - GTK_SORT_ASCENDING or GTK_SORT_DESCENDING *
231 \********************************************************************/
232 void
233 gnc_query_sort_order (GNCQueryView *qview, gint column, GtkSortType order)
234 {
235  GtkTreeSortable *sortable;
236  gint sortcol;
237 
238  g_return_if_fail (qview != NULL);
239  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
240 
241  sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model (GTK_TREE_VIEW(qview)));
242 
243  if((column > qview->num_columns) || (column == 0) )
244  sortcol = 1;
245  else
246  sortcol = column;
247 
248  gtk_tree_sortable_set_sort_column_id (sortable, sortcol, order);
249 }
250 
251 static void
252 gnc_query_sort_cb (GtkTreeSortable *sortable, gpointer user_data)
253 {
254  GNCQueryView *qview = GNC_QUERY_VIEW(user_data);
255  GtkSortType type;
256  gint sortcol;
257  gboolean new_column = FALSE;
258 
259  g_return_if_fail (qview != NULL);
260  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
261  g_return_if_fail (qview->query != NULL);
262 
263  gtk_tree_sortable_get_sort_column_id (sortable, &sortcol, &type);
264 
265  /* We need to subtract 1 for the added pointer column in the liststore
266  which is not displayed to align back to params */
267  sortcol = sortcol - 1;
268 
269  if(type == GTK_SORT_ASCENDING)
270  qview->increasing = TRUE;
271  else
272  qview->increasing = FALSE;
273 
274  /* Is this a new column or a re-click on the existing column? */
275  new_column = (qview->sort_column != sortcol);
276 
277  /* Save the column */
278  qview->sort_column = sortcol;
279 
280  gnc_query_view_set_query_sort (qview, new_column);
281 }
282 
283 static void
284 gnc_query_view_init_view (GNCQueryView *qview)
285 {
286  GtkTreeView *view = GTK_TREE_VIEW(qview);
287  GtkTreeSortable *sortable;
288  GtkTreeSelection *selection;
289  GtkTreeViewColumn *col;
290  GtkCellRenderer *renderer;
291  GList *node;
292  gint i;
293 
294  sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model (GTK_TREE_VIEW(view)));
295 
296  /* compute the number of columns and fill in the rest of the view */
297  qview->num_columns = g_list_length (qview->column_params);
298 
299  // Set grid lines option to preference
300  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
301 
302  for (i = 0, node = qview->column_params; node; node = node->next, i++)
303  {
304  const char *type;
305  gfloat algn = 0;
306  GNCSearchParamSimple *param = node->data;
307 
308  g_assert (GNC_IS_SEARCH_PARAM_SIMPLE(param));
309 
310  col = gtk_tree_view_column_new ();
311 
312  /* Set the column title */
313  gtk_tree_view_column_set_title (col, gnc_search_param_get_title (GNC_SEARCH_PARAM (param)));
314 
315  /* pack tree view column into tree view */
316  gtk_tree_view_append_column (view, col);
317 
318  /* Get justification */
319  if (gnc_search_param_get_justify (GNC_SEARCH_PARAM (param)) == GTK_JUSTIFY_CENTER)
320  algn = 0.5;
321  else if (gnc_search_param_get_justify (GNC_SEARCH_PARAM (param)) == GTK_JUSTIFY_RIGHT)
322  {
323  /* GTK_JUSTIFY_RIGHT is only used for monetary values so right align
324  * the column title and data for both ltr and rtl */
325  if (gtk_widget_get_direction (GTK_WIDGET(view)) != GTK_TEXT_DIR_RTL)
326  algn = 1.0;
327  }
328 
329  /* Set the column title alignment to that of the column */
330  gtk_tree_view_column_set_alignment (col, algn);
331 
332  /* Set column resizable */
333  if (gnc_search_param_get_non_resizeable (GNC_SEARCH_PARAM (param)))
334  {
335  gtk_tree_view_column_set_resizable (col, FALSE);
336  gtk_tree_view_column_set_expand (col, FALSE);
337  }
338  else
339  gtk_tree_view_column_set_resizable (col, TRUE);
340 
341  /* Set column clickable */
342  if (gnc_search_param_get_passive (GNC_SEARCH_PARAM (param)))
343  gtk_tree_view_column_set_clickable (col, FALSE);
344  else
345  {
346  gtk_tree_view_column_set_clickable (col, TRUE);
347  /* Add sortable columns */
348  gtk_tree_view_column_set_sort_column_id (col, i+1);
349  gtk_tree_sortable_set_sort_func (sortable, i+1,
350  sort_iter_compare_func,
351  GINT_TO_POINTER(i+1), NULL);
352  }
353 
354  type = gnc_search_param_get_param_type (((GNCSearchParam *) param));
355 
356  if (g_strcmp0 (type, QOF_TYPE_BOOLEAN) == 0)
357  {
358  renderer = gtk_cell_renderer_toggle_new ();
359 
360  /* pack cell renderer toggle into tree view column */
361  gtk_tree_view_column_pack_start (col, renderer, TRUE);
362  gtk_tree_view_column_add_attribute (col, renderer, "active", i+1);
363  g_object_set (renderer, "xalign", algn, NULL);
364  g_object_set_data (G_OBJECT(renderer), "column", GINT_TO_POINTER(i+1));
365  g_signal_connect (renderer, "toggled",
366  G_CALLBACK(gnc_query_view_toggled_cb), view);
367  }
368  else
369  {
370  renderer = gtk_cell_renderer_text_new ();
371 
372  /* pack cell renderer text into tree view column */
373  gtk_tree_view_column_pack_start (col, renderer, TRUE);
374  gtk_tree_view_column_add_attribute (col, renderer, "text", i+1);
375  g_object_set (renderer, "xalign", algn, NULL);
376  g_object_set_data (G_OBJECT(renderer), "column", GINT_TO_POINTER(i+1));
377  }
378  }
379 
380  /* set initial sort order */
381  gtk_tree_sortable_set_default_sort_func (sortable, NULL, NULL, NULL);
382  gtk_tree_sortable_set_sort_column_id (sortable, 1, GTK_SORT_DESCENDING);
383 
384  g_signal_connect (sortable, "sort-column-changed",
385  G_CALLBACK(gnc_query_sort_cb),
386  view);
387 
388  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
389  g_signal_connect (selection, "changed",
390  G_CALLBACK(gnc_query_view_select_row_cb),
391  NULL);
392 
393  g_signal_connect (view, "row-activated",
394  G_CALLBACK(gnc_query_view_double_click_cb),
395  NULL);
396 }
397 
398 static void
399 gnc_query_view_class_init (GNCQueryViewClass *klass)
400 {
401  GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
402 
403  query_view_signals[COLUMN_TOGGLED] =
404  g_signal_new("column_toggled",
405  G_TYPE_FROM_CLASS(widget_class),
406  G_SIGNAL_RUN_FIRST,
407  G_STRUCT_OFFSET(GNCQueryViewClass, column_toggled),
408  NULL, NULL,
409  g_cclosure_marshal_VOID__POINTER,
410  G_TYPE_NONE,
411  1,
412  G_TYPE_POINTER);
413 
414  query_view_signals[ROW_SELECTED] =
415  g_signal_new("row_selected",
416  G_TYPE_FROM_CLASS(widget_class),
417  G_SIGNAL_RUN_FIRST,
418  G_STRUCT_OFFSET(GNCQueryViewClass, row_selected),
419  NULL, NULL,
420  g_cclosure_marshal_VOID__POINTER,
421  G_TYPE_NONE,
422  1,
423  G_TYPE_POINTER);
424 
425  query_view_signals[DOUBLE_CLICK_ENTRY] =
426  g_signal_new("double_click_entry",
427  G_TYPE_FROM_CLASS(widget_class),
428  G_SIGNAL_RUN_FIRST,
429  G_STRUCT_OFFSET(GNCQueryViewClass, double_click_entry),
430  NULL, NULL,
431  g_cclosure_marshal_VOID__POINTER,
432  G_TYPE_NONE,
433  1,
434  G_TYPE_POINTER);
435 
436  widget_class->destroy = gnc_query_view_destroy;
437 
438  klass->column_toggled = NULL;
439  klass->row_selected = NULL;
440  klass->double_click_entry = NULL;
441 }
442 
443 static void
444 gnc_query_view_select_row_cb (GtkTreeSelection *selection, gpointer user_data)
445 {
446  GNCQueryView *qview = GNC_QUERY_VIEW(gtk_tree_selection_get_tree_view (selection));
447  gint number_of_rows = gtk_tree_selection_count_selected_rows (selection);
448 
449  g_signal_emit (qview, query_view_signals[ROW_SELECTED], 0,
450  GINT_TO_POINTER(number_of_rows));
451 }
452 
453 static void
454 gnc_query_view_double_click_cb (GtkTreeView *view,
455  GtkTreePath *path,
456  GtkTreeViewColumn *column,
457  gpointer user_data)
458 {
459  GNCQueryView *qview = GNC_QUERY_VIEW(view);
460  GtkTreeModel *model;
461  GtkTreeIter iter;
462  gpointer entry = NULL;
463 
464  model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
465 
466  if (gtk_tree_model_get_iter (model, &iter, path))
467  gtk_tree_model_get (model, &iter, 0, &entry, -1);
468 
469  g_signal_emit (qview, query_view_signals[DOUBLE_CLICK_ENTRY], 0, entry);
470 }
471 
472 static void
473 gnc_query_view_toggled_cb (GtkCellRendererToggle *cell_renderer,
474  gchar *path,
475  gpointer user_data)
476 {
477  GNCQueryView *qview = GNC_QUERY_VIEW(user_data);
478  GtkTreeModel *model;
479  GtkTreeIter iter;
480  GtkTreePath *treepath;
481  gint *indices;
482  gpointer entry = NULL;
483  gboolean toggled;
484  gint column;
485 
486  model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
487 
488  column = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(cell_renderer), "column"));
489 
490  toggled = gtk_cell_renderer_toggle_get_active (cell_renderer);
491 
492  treepath = gtk_tree_path_new_from_string (path);
493 
494  if (gtk_tree_model_get_iter (model, &iter, treepath))
495  {
496  gtk_tree_model_get (model, &iter, 0, &entry, -1);
497  indices = gtk_tree_path_get_indices (treepath);
498  qview->toggled_row = indices[0];
499  qview->toggled_column = column;
500 
501  if(toggled)
502  g_signal_emit (qview, query_view_signals[COLUMN_TOGGLED], 0, GINT_TO_POINTER(0));
503  else
504  g_signal_emit (qview, query_view_signals[COLUMN_TOGGLED], 0, GINT_TO_POINTER(1));
505  }
506  gtk_tree_path_free (treepath);
507 }
508 
509 static void
510 gnc_query_view_destroy (GtkWidget *widget)
511 {
512  GNCQueryView *qview = GNC_QUERY_VIEW(widget);
513  GNCQueryViewPrivate *priv;
514 
515  priv = GNC_QUERY_VIEW_GET_PRIVATE(qview);
516  if (priv->component_id > 0)
517  {
518  gnc_unregister_gui_component (priv->component_id);
519  priv->component_id = 0;
520  }
521 
522  /* Remove the query */
523  if (qview->query)
524  {
525  qof_query_destroy (qview->query);
526  qview->query = NULL;
527  }
528 
529  GTK_WIDGET_CLASS(gnc_query_view_parent_class)->destroy (widget);
530 }
531 
532 gint
533 gnc_query_view_get_num_entries (GNCQueryView *qview)
534 {
535  GtkTreeModel *model;
536 
537  g_return_val_if_fail (qview != NULL, 0);
538  g_return_val_if_fail (GNC_IS_QUERY_VIEW(qview), 0);
539 
540  model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
541  return gtk_tree_model_iter_n_children (model, NULL);
542 }
543 
544 gpointer
545 gnc_query_view_get_selected_entry (GNCQueryView *qview)
546 {
547  gpointer entry = NULL;
548  GList *entries = NULL;
549  gint num_entries = 0;
550 
551  g_return_val_if_fail (qview != NULL, NULL);
552  g_return_val_if_fail (GNC_IS_QUERY_VIEW(qview), NULL);
553 
554  entries = gnc_query_view_get_selected_entry_list (qview);
555  if (entries)
556  entry = entries->data;
557 
558  num_entries = g_list_length (entries);
559  if (num_entries > 1)
560  PWARN ("Expected only one selected entry but found %i. "
561  "Discarding all but the first one.", num_entries);
562 
563  g_list_free (entries);
564 
565  return entry;
566 }
567 
568 typedef struct
569 {
570  GList *entries;
571 } acc_data;
572 
573 static void
574 accumulate_entries (GtkTreeModel *model, GtkTreePath *path,
575  GtkTreeIter *iter, gpointer data)
576 {
577  acc_data *acc_entries = (acc_data*)data;
578  gpointer entry = NULL;
579  GList *entries = acc_entries->entries;
580 
581  gtk_tree_model_get (model, iter, 0, &entry, -1);
582  entries = g_list_prepend (entries, entry);
583  acc_entries->entries = entries;
584 }
585 
586 GList *
587 gnc_query_view_get_selected_entry_list (GNCQueryView *qview)
588 {
589  GtkTreeSelection *selection;
590  acc_data acc_entries;
591 
592  g_return_val_if_fail (qview != NULL, NULL);
593  g_return_val_if_fail (GNC_IS_QUERY_VIEW(qview), NULL);
594 
595  acc_entries.entries = NULL;
596  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
597  gtk_tree_selection_selected_foreach (selection, accumulate_entries,
598  &acc_entries);
599  acc_entries.entries = g_list_reverse (acc_entries.entries);
600  return acc_entries.entries;
601 }
602 
603 void
604 gnc_query_use_scroll_to_selection (GNCQueryView *qview, gboolean scroll)
605 {
606  g_return_if_fail (qview != NULL);
607  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
608 
609  qview->use_scroll_to_selection = scroll;
610 }
611 
612 static void
613 scroll_to_selection (GNCQueryView *qview, gboolean override_scroll)
614 {
615  GtkTreeSelection *selection;
616  GList *path_list, *node;
617 
618  if (!qview->use_scroll_to_selection && !override_scroll)
619  return;
620 
621  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
622 
623  /* Ensure last selected item, if any, can be seen */
624  path_list = gtk_tree_selection_get_selected_rows (selection, NULL);
625  node = g_list_last (path_list);
626 
627  if (node)
628  {
629  GtkTreePath *tree_path = node->data;
630  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(qview),
631  tree_path, NULL, FALSE, 0.0, 0.0);
632  }
633  g_list_free_full (path_list, (GDestroyNotify) gtk_tree_path_free);
634 }
635 
636 void
637 gnc_query_scroll_to_selection (GNCQueryView *qview)
638 {
639  g_return_if_fail (qview != NULL);
640  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
641 
642  scroll_to_selection (qview, FALSE);
643 }
644 
645 void
646 gnc_query_force_scroll_to_selection (GNCQueryView *qview)
647 {
648  g_return_if_fail (qview != NULL);
649  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
650 
651  scroll_to_selection (qview, TRUE);
652 }
653 
654 static void
655 gnc_query_view_refresh_selected (GNCQueryView *qview, GList *old_entry)
656 {
657  GtkTreeModel *model;
658  GtkTreeIter iter;
659  GtkTreeSelection *selection;
660  GList *node;
661  gboolean valid;
662 
663  g_return_if_fail (qview != NULL);
664  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
665 
666  model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
667  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
668 
669  if (g_list_length (old_entry) > 0)
670  {
671  /* Walk the list of old entries */
672  for (node = old_entry; node; node = node->next)
673  {
674  gpointer pointer;
675 
676  valid = gtk_tree_model_get_iter_first (model, &iter);
677 
678  while (valid)
679  {
680  // Walk through the liststore, reading each row
681  gtk_tree_model_get (model, &iter, 0, &pointer, -1);
682 
683  if (pointer == node->data)
684  {
685  gtk_tree_selection_select_iter (selection, &iter);
686  break;
687  }
688  valid = gtk_tree_model_iter_next (model, &iter);
689  }
690  }
691  gnc_query_scroll_to_selection (qview);
692  }
693 }
694 
695 /********************************************************************\
696  * gnc_query_view_refresh *
697  * refreshes the view *
698  * *
699  * Args: qview - view to refresh *
700  * Returns: nothing *
701 \********************************************************************/
702 void
703 gnc_query_view_refresh (GNCQueryView *qview)
704 {
705  GtkTreeModel *model;
706  GList *selected_entries;
707 
708  g_return_if_fail (qview != NULL);
709  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
710 
711  selected_entries = gnc_query_view_get_selected_entry_list (qview);
712  model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
713  gtk_list_store_clear (GTK_LIST_STORE(model));
714 
715  gnc_query_view_fill (qview);
716  gnc_query_view_refresh_selected (qview, selected_entries);
717  g_list_free (selected_entries);
718 }
719 
720 /********************************************************************\
721  * gnc_query_view_set_query_sort *
722  * sets the sorting order of entries in the view *
723  * *
724  * Args: qview - view to change the sort order for *
725  * new_column - is this a new column (so should we set the *
726  * query sort order or just set the 'increasing' *
727  * Returns: nothing *
728 \********************************************************************/
729 static void
730 gnc_query_view_set_query_sort (GNCQueryView *qview, gboolean new_column)
731 {
732  gboolean sort_order = qview->increasing;
733  GList *node;
734  GNCSearchParamSimple *param;
735 
736  /* Find the column parameter definition */
737  node = g_list_nth (qview->column_params, qview->sort_column);
738  param = node->data;
739 
740  g_assert (GNC_IS_SEARCH_PARAM_SIMPLE(param));
741 
742  /* If param values are based on a param function, sorting is not possible */
743  if (gnc_search_param_has_param_fcn (param))
744  {
745  gnc_query_view_refresh (qview);
746  return;
747  }
748 
749  /* If we're asked to invert numerics, and if this is a numeric or
750  * debred column, then invert the sort order.
751  */
752  if (qview->numeric_inv_sort)
753  {
754  const char *type = gnc_search_param_get_param_type ((GNCSearchParam *) param);
755  if (!g_strcmp0(type, QOF_TYPE_NUMERIC) ||
756  !g_strcmp0(type, QOF_TYPE_DEBCRED))
757  sort_order = !sort_order;
758  }
759 
760  /* Set the sort order for the engine, if the key changed */
761  if (new_column)
762  {
763  GSList *p1, *p2;
764 
765  p1 = gnc_search_param_get_param_path (param);
766  p2 = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
767  qof_query_set_sort_order (qview->query, p1, p2, NULL);
768  }
769 
770  qof_query_set_sort_increasing (qview->query,
771  sort_order,
772  sort_order,
773  sort_order);
774 
775  gnc_query_view_refresh (qview);
776 }
777 
778 /********************************************************************\
779  * gnc_query_set_expand_column *
780  * sets the column that expands to take up free space starting *
781  * from 0 *
782  * *
783  * Args: qview - view to change the sort order for *
784  * column - the tree view column to set to expand *
785  * Returns: nothing *
786 \********************************************************************/
787 void
788 gnc_query_set_expand_column (GNCQueryView *qview, gint column)
789 {
790  g_return_if_fail (qview != NULL);
791  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
792 
793  GtkTreeView *view = GTK_TREE_VIEW(qview);
794 
795  gint num_columns = gtk_tree_view_get_n_columns (view);
796 
797  if (column >= 0 && column < num_columns)
798  {
799  GtkTreeViewColumn *tree_column = gtk_tree_view_get_column (view, column);
800  gtk_tree_view_column_set_expand (tree_column, TRUE);
801  }
802 }
803 
804 /********************************************************************\
805  * gnc_query_view_fill *
806  * Add all items to the list store *
807  * *
808  * Args: qview - view to add item to *
809  * Returns: nothing *
810 \********************************************************************/
811 static void
812 gnc_query_view_fill (GNCQueryView *qview)
813 {
814  GNCQueryViewPrivate *priv;
815  GtkTreeModel *model;
816  GtkTreeIter iter;
817  GList *entries, *item;
818  const GncGUID *guid;
819  gint i;
820 
821  /* Clear all watches */
822  priv = GNC_QUERY_VIEW_GET_PRIVATE(qview);
823  gnc_gui_component_clear_watches (priv->component_id);
824 
825  entries = qof_query_run (qview->query);
826 
827  model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
828 
829  for (item = entries; item; item = item->next)
830  {
831  GList *node;
832  const QofParam *gup;
833  QofParam *qp = NULL;
834 
835  /* Add a row to the list store */
836  gtk_list_store_append (GTK_LIST_STORE(model), &iter);
837  /* Add a pointer to the data in the first column of the list store */
838  gtk_list_store_set (GTK_LIST_STORE(model), &iter, 0, item->data, -1);
839 
840  for (i = 0, node = qview->column_params; node; node = node->next)
841  {
842  gboolean result;
843  GNCSearchParamSimple *param = node->data;
844  GSList *converters = NULL;
845  const char *type = gnc_search_param_get_param_type ((GNCSearchParam *) param);
846  gpointer res = item->data;
847  gchar *qofstring;
848 
849  g_assert (GNC_IS_SEARCH_PARAM_SIMPLE(param));
850  converters = gnc_search_param_get_converters (param);
851 
852  /* Test for boolean type */
853  if (g_strcmp0 (type, QOF_TYPE_BOOLEAN) == 0)
854  {
855  result = (gboolean) GPOINTER_TO_INT(gnc_search_param_compute_value (param, res));
856  gtk_list_store_set (GTK_LIST_STORE(model), &iter, i + 1, result, -1);
857  i++;
858  continue;
859  }
860 
861  /* Do all the object conversions */
862  for (; converters; converters = converters->next)
863  {
864  qp = converters->data;
865  if (converters->next)
866  res = (qp->param_getfcn)(res, qp);
867  }
868 
869  /* Now convert this to a text value for the row */
870  if (qp && (g_strcmp0 (type, QOF_TYPE_DEBCRED) == 0 ||
871  g_strcmp0 (type, QOF_TYPE_NUMERIC) == 0))
872  {
873 
874  gnc_numeric (*nfcn)(gpointer, QofParam *) =
875  (gnc_numeric(*)(gpointer, QofParam *))(qp->param_getfcn);
876  gnc_numeric value = nfcn (res, qp);
877 
878  if (qview->numeric_abs)
879  value = gnc_numeric_abs (value);
880  gtk_list_store_set (GTK_LIST_STORE(model), &iter, i + 1,
881  xaccPrintAmount (value, gnc_default_print_info (FALSE)), -1);
882  }
883  else
884  {
885  qofstring = qof_query_core_to_string (type, res, qp);
886  gtk_list_store_set (GTK_LIST_STORE(model), &iter, i + 1, qofstring , -1);
887  g_free (qofstring);
888  }
889  i++;
890  }
891  /* and set a watcher on this item */
892  gup = priv->get_guid;
893  guid = (const GncGUID*)((gup->param_getfcn)(item->data, gup));
894  gnc_gui_component_watch_entity (priv->component_id, guid,
895  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
896  }
897 }
898 
899 /********************************************************************\
900  * gnc_query_view_unselect_all *
901  * unselect all items in the view *
902  * *
903  * Args: qview - view to unselect all *
904  * Returns: nothing *
905 \********************************************************************/
906 void
907 gnc_query_view_unselect_all (GNCQueryView *qview)
908 {
909  GtkTreeSelection *selection;
910 
911  g_return_if_fail (qview != NULL);
912  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
913 
914  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(qview));
915  gtk_tree_selection_unselect_all (selection);
916 }
917 
918 gboolean gnc_query_view_item_in_view (GNCQueryView *qview, gpointer item)
919 {
920  GtkTreeModel *model;
921  GtkTreeIter iter;
922  gboolean valid;
923  gpointer pointer;
924 
925  g_return_val_if_fail (qview, FALSE);
926  g_return_val_if_fail (item, FALSE);
927  g_return_val_if_fail (GNC_IS_QUERY_VIEW(qview), FALSE);
928 
929  model = gtk_tree_view_get_model (GTK_TREE_VIEW(qview));
930  valid = gtk_tree_model_get_iter_first (model, &iter);
931 
932  while (valid)
933  {
934  // Walk through the list, reading each row
935  gtk_tree_model_get (model, &iter, 0, &pointer, -1);
936 
937  if (pointer == item)
938  return TRUE;
939 
940  valid = gtk_tree_model_iter_next (model, &iter);
941  }
942  return FALSE;
943 }
944 
945 void
946 gnc_query_view_set_numerics (GNCQueryView *qview, gboolean abs, gboolean inv_sort)
947 {
948  g_return_if_fail (qview);
949  g_return_if_fail (GNC_IS_QUERY_VIEW(qview));
950 
951  qview->numeric_abs = abs;
952  qview->numeric_inv_sort = inv_sort;
953 }
utility functions for the GnuCash UI
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
When a query is run, the results are sorted before being returned.
Definition: qofquery.cpp:1249
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
QofQuery * qof_query_copy(QofQuery *q)
Make a copy of the indicated query.
Definition: qofquery.cpp:1018
void qof_query_set_sort_increasing(QofQuery *q, gboolean prim_inc, gboolean sec_inc, gboolean tert_inc)
When a query is run, the results are sorted before being returned.
Definition: qofquery.cpp:1280
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
const QofParam * qof_class_get_parameter(QofIdTypeConst obj_name, const char *parameter)
Return the registered Parameter Definition for the requested parameter.
Definition: qofclass.cpp:136
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
char * qof_query_core_to_string(QofType type, gpointer object, QofParam *getter)
Return a printable string for a core data object.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
GList * qof_query_run(QofQuery *query)
Perform the query, return the results.
QofIdType qof_query_get_search_for(const QofQuery *q)
Return the type of data we&#39;re querying for.
Definition: qofquery.cpp:1380
#define QUERY_DEFAULT_SORT
Default sort object type.
Definition: qofquery.h:105
The type used to store guids in C.
Definition: guid.h:75
A Query.
Definition: qofquery.cpp:74