GnuCash  4.11-148-gc20d717b33+
gnc-tree-view-price.c
1 /********************************************************************\
2  * gnc-tree-view-price.c -- GtkTreeView implementation to display *
3  * prices in a GtkTreeView. *
4  * Copyright (C) 2003,2005 David Hampton <hampton@employees.org> *
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 
25 #include <config.h>
26 
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <string.h>
30 
31 #include "gnc-tree-view.h"
32 #include "gnc-tree-model-price.h"
33 #include "gnc-tree-view-price.h"
34 
35 #include "gnc-pricedb.h"
36 #include "gnc-component-manager.h"
37 #include "gnc-engine.h"
38 #include "gnc-glib-utils.h"
39 #include "gnc-gnome-utils.h"
40 #include "gnc-icons.h"
41 #include "gnc-ui-util.h"
42 
43 
46 /* This static indicates the debugging module that this .o belongs to. */
47 static QofLogModule log_module = GNC_MOD_GUI;
48 
50 static void gnc_tree_view_price_class_init (GncTreeViewPriceClass *klass);
51 static void gnc_tree_view_price_init (GncTreeViewPrice *view);
52 static void gnc_tree_view_price_finalize (GObject *object);
53 static void gnc_tree_view_price_destroy (GtkWidget *widget);
54 
56 {
57  gpointer dummy;
59 
60 #define GNC_TREE_VIEW_PRICE_GET_PRIVATE(o) \
61  ((GncTreeViewPricePrivate*)gnc_tree_view_price_get_instance_private((GncTreeViewPrice*)o))
62 
63 
64 /************************************************************/
65 /* g_object required functions */
66 /************************************************************/
67 
68 static GObjectClass *parent_class = NULL;
69 
70 G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewPrice, gnc_tree_view_price, GNC_TYPE_TREE_VIEW)
71 
72 static void
73 gnc_tree_view_price_class_init (GncTreeViewPriceClass *klass)
74 {
75  GObjectClass *o_class;
76  GtkWidgetClass *widget_class;
77 
78  parent_class = g_type_class_peek_parent (klass);
79 
80  o_class = G_OBJECT_CLASS (klass);
81  widget_class = GTK_WIDGET_CLASS (klass);
82 
83  /* GObject signals */
84  o_class->finalize = gnc_tree_view_price_finalize;
85 
86  /* GtkWidget signals */
87  widget_class->destroy = gnc_tree_view_price_destroy;
88 }
89 
90 static void
91 gnc_tree_view_price_init (GncTreeViewPrice *view)
92 {
93 }
94 
95 static void
96 gnc_tree_view_price_finalize (GObject *object)
97 {
98  ENTER("view %p", object);
99  gnc_leave_return_if_fail (object != NULL);
100  gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_PRICE (object));
101 
102  if (G_OBJECT_CLASS (parent_class)->finalize)
103  (* G_OBJECT_CLASS (parent_class)->finalize) (object);
104  LEAVE(" ");
105 }
106 
107 static void
108 gnc_tree_view_price_destroy (GtkWidget *widget)
109 {
110  ENTER("view %p", widget);
111  gnc_leave_return_if_fail (widget != NULL);
112  gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_PRICE (widget));
113 
114  if (GTK_WIDGET_CLASS (parent_class)->destroy)
115  (* GTK_WIDGET_CLASS (parent_class)->destroy) (widget);
116  LEAVE(" ");
117 }
118 
119 
120 /************************************************************/
121 /* sort functions */
122 /************************************************************/
123 
124 static gboolean
125 get_prices (GtkTreeModel *f_model,
126  GtkTreeIter *f_iter_a,
127  GtkTreeIter *f_iter_b,
128  GNCPrice **price_a,
129  GNCPrice **price_b)
130 {
131  GncTreeModelPrice *model;
132  GtkTreeModel *tree_model;
133  GtkTreeIter iter_a, iter_b;
134 
135  tree_model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
136  model = GNC_TREE_MODEL_PRICE(tree_model);
137 
138  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
139  &iter_a,
140  f_iter_a);
141 
142  /* The iters must point to prices for this to be meaningful */
143  if (!gnc_tree_model_price_iter_is_price (model, &iter_a))
144  return FALSE;
145 
146  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
147  &iter_b,
148  f_iter_b);
149 
150  *price_a = gnc_tree_model_price_get_price (model, &iter_a);
151  *price_b = gnc_tree_model_price_get_price (model, &iter_b);
152  return TRUE;
153 }
154 
155 static gint
156 sort_ns_or_cm (GtkTreeModel *f_model,
157  GtkTreeIter *f_iter_a,
158  GtkTreeIter *f_iter_b)
159 {
160  GncTreeModelPrice *model;
161  GtkTreeModel *tree_model;
162  GtkTreeIter iter_a, iter_b;
163  gnc_commodity_namespace *ns_a, *ns_b;
164  gnc_commodity *comm_a, *comm_b;
165 
166  tree_model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
167  model = GNC_TREE_MODEL_PRICE(tree_model);
168 
169  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
170  &iter_a,
171  f_iter_a);
172  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
173  &iter_b,
174  f_iter_b);
175 
176  if (gnc_tree_model_price_iter_is_namespace (model, &iter_a))
177  {
178  ns_a = gnc_tree_model_price_get_namespace (model, &iter_a);
179  ns_b = gnc_tree_model_price_get_namespace (model, &iter_b);
182  }
183 
184  comm_a = gnc_tree_model_price_get_commodity (model, &iter_a);
185  comm_b = gnc_tree_model_price_get_commodity (model, &iter_b);
187  gnc_commodity_get_mnemonic (comm_b));
188 }
189 
190 static gint
191 default_sort (GNCPrice *price_a, GNCPrice *price_b)
192 {
193  gnc_commodity *curr_a, *curr_b;
194  time64 time_a, time_b;
195  gint result;
196 
197  /* Primary sort (i.e. commodity name) handled by the tree structure. */
198 
199  /* secondary sort: currency */
200  curr_a = gnc_price_get_currency (price_a);
201  curr_b = gnc_price_get_currency (price_b);
202 
204  gnc_commodity_get_namespace (curr_b));
205  if (result != 0) return result;
206 
208  gnc_commodity_get_mnemonic (curr_b));
209  if (result != 0) return result;
210 
211  /* tertiary sort: time */
212  time_a = gnc_price_get_time64 (price_a);
213  time_b = gnc_price_get_time64 (price_b);
214  result = time_a < time_b ? -1 : time_a > time_b ? 1 : 0;
215  if (result)
216  /* Reverse the result to present the most recent quote first. */
217  return -result;
218 
219  /* last sort: value */
220  return gnc_numeric_compare (gnc_price_get_value (price_a),
221  gnc_price_get_value (price_b));
222 }
223 
224 static gint
225 sort_by_name (GtkTreeModel *f_model,
226  GtkTreeIter *f_iter_a,
227  GtkTreeIter *f_iter_b,
228  gpointer user_data)
229 {
230  GNCPrice *price_a, *price_b;
231 
232  if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
233  return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
234 
235  return default_sort (price_a, price_b);
236 }
237 
238 static gint
239 sort_by_date (GtkTreeModel *f_model,
240  GtkTreeIter *f_iter_a,
241  GtkTreeIter *f_iter_b,
242  gpointer user_data)
243 {
244  GNCPrice *price_a, *price_b;
245  time64 time_a, time_b;
246  gboolean result;
247 
248  if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
249  return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
250 
251  /* sort by time first */
252  time_a = gnc_price_get_time64 (price_a);
253  time_b = gnc_price_get_time64 (price_b);
254  result = time_a < time_b ? -1 : time_a > time_b ? 1 : 0;
255  if (result)
256  /* Reverse the result to present the most recent quote first. */
257  return -result;
258 
259  return default_sort (price_a, price_b);
260 }
261 
262 static gint
263 sort_by_source (GtkTreeModel *f_model,
264  GtkTreeIter *f_iter_a,
265  GtkTreeIter *f_iter_b,
266  gpointer user_data)
267 {
268  GNCPrice *price_a, *price_b;
269  gint result;
270 
271  if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
272  return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
273 
274  /* sort by source first */
275  result = gnc_price_get_source (price_a) < gnc_price_get_source (price_b);
276  if (result != 0)
277  return result;
278 
279  return default_sort (price_a, price_b);
280 }
281 
282 static gint
283 sort_by_type (GtkTreeModel *f_model,
284  GtkTreeIter *f_iter_a,
285  GtkTreeIter *f_iter_b,
286  gpointer user_data)
287 {
288  GNCPrice *price_a, *price_b;
289  gint result;
290 
291  if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
292  return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
293 
294  /* sort by source first */
295  result = safe_utf8_collate (gnc_price_get_typestr (price_a),
296  gnc_price_get_typestr (price_b));
297  if (result != 0)
298  return result;
299 
300  return default_sort (price_a, price_b);
301 }
302 
303 static gint
304 sort_by_value (GtkTreeModel *f_model,
305  GtkTreeIter *f_iter_a,
306  GtkTreeIter *f_iter_b,
307  gpointer user_data)
308 {
309  gnc_commodity *comm_a, *comm_b;
310  GNCPrice *price_a, *price_b;
311  gboolean result;
312  gint value;
313 
314  if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
315  return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
316 
317  /*
318  * Sorted by commodity because of the tree structure. Now sort by
319  * currency so we're only comparing numbers in the same currency
320  * denomination.
321  */
322  comm_a = gnc_price_get_currency (price_a);
323  comm_b = gnc_price_get_currency (price_b);
324  if (comm_a && comm_b)
325  {
327  gnc_commodity_get_namespace (comm_b));
328  if (value != 0)
329  return value;
331  gnc_commodity_get_mnemonic (comm_b));
332  if (value != 0)
333  return value;
334  }
335 
336  /*
337  * Now do the actual price comparison now we're sure that its an
338  * apples to apples comparison.
339  */
340  result = gnc_numeric_compare (gnc_price_get_value (price_a),
341  gnc_price_get_value (price_b));
342  if (result)
343  return result;
344 
345  return default_sort (price_a, price_b);
346 }
347 
348 
349 /************************************************************/
350 /* New View Creation */
351 /************************************************************/
352 
353 /*
354  * Create a new price tree view with (optional) top level root node.
355  * This view will be based on a model that is common to all view of
356  * the same set of books, but will have its own private filter on that
357  * model.
358  */
359 GtkTreeView *
360 gnc_tree_view_price_new (QofBook *book,
361  const gchar *first_property_name,
362  ...)
363 {
364  GncTreeView *view;
365  GtkTreeModel *model, *f_model, *s_model;
366  GtkTreeViewColumn *col;
367  GNCPriceDB *price_db;
368  va_list var_args;
369  const gchar *sample_text;
370  gchar *sample_text2;
371 
372  ENTER(" ");
373  /* Create/get a pointer to the existing model for this set of books. */
374  price_db = gnc_pricedb_get_db(book);
375  model = gnc_tree_model_price_new (book, price_db);
376 
377  /* Set up the view private filter on the common model. */
378  f_model = gtk_tree_model_filter_new (model, NULL);
379  g_object_unref(G_OBJECT(model));
380  s_model = gtk_tree_model_sort_new_with_model (f_model);
381  g_object_unref(G_OBJECT(f_model));
382 
383  /* Create our view */
384  view = g_object_new (GNC_TYPE_TREE_VIEW_PRICE,
385  "name", "gnc-id-price-tree", NULL);
386  gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
387  g_object_unref(G_OBJECT(s_model));
388 
389  DEBUG("model ref count is %d", G_OBJECT(model)->ref_count);
390  DEBUG("f_model ref count is %d", G_OBJECT(f_model)->ref_count);
391  DEBUG("s_model ref count is %d", G_OBJECT(s_model)->ref_count);
392 
394  sample_text2 = g_strdup_printf("%s%s", sample_text, sample_text);
396  view, _("Security"), "security", NULL, sample_text2,
397  GNC_TREE_MODEL_PRICE_COL_COMMODITY,
398  GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
399  sort_by_name);
400  g_free(sample_text2);
402  view, _("Currency"), "currency", NULL, sample_text,
403  GNC_TREE_MODEL_PRICE_COL_CURRENCY,
404  GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
405  sort_by_name);
406  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
408  view, _("Date"), "date", NULL, "2005-05-20",
409  GNC_TREE_MODEL_PRICE_COL_DATE,
410  GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
411  sort_by_date);
412  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
414  view, _("Source"), "source", NULL, "Finance::Quote",
415  GNC_TREE_MODEL_PRICE_COL_SOURCE,
416  GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
417  sort_by_source);
418  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
420  view, _("Type"), "type", NULL, "last",
421  GNC_TREE_MODEL_PRICE_COL_TYPE,
422  GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
423  sort_by_type);
424  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
426  view, _("Price"), "price", "100.00000",
427  GNC_TREE_MODEL_PRICE_COL_VALUE,
428  GNC_TREE_VIEW_COLUMN_COLOR_NONE,
429  GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
430  sort_by_value);
431  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
432 
434 
435  /* Set properties */
436  va_start (var_args, first_property_name);
437  g_object_set_valist (G_OBJECT(view), first_property_name, var_args);
438  va_end (var_args);
439 
440  /* Sort on the commodity column by default. This allows for a consistent
441  * sort if commodities are removed and re-added from the model. */
442  if (!gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(s_model),
443  NULL, NULL))
444  {
445  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
446  GNC_TREE_MODEL_PRICE_COL_COMMODITY,
447  GTK_SORT_ASCENDING);
448  }
449 
450  gtk_widget_show(GTK_WIDGET(view));
451  LEAVE(" %p", view);
452  return GTK_TREE_VIEW(view);
453 }
454 
455 /************************************************************/
456 /* Auxiliary Functions */
457 /************************************************************/
458 
459 #define debug_path(fn, path) { \
460  gchar *path_string = gtk_tree_path_to_string(path); \
461  fn("tree path %s", path_string); \
462  g_free(path_string); \
463  }
464 
465 #if 0 /* Not Used */
466 static gboolean
467 gnc_tree_view_price_get_iter_from_price (GncTreeViewPrice *view,
468  GNCPrice *price,
469  GtkTreeIter *s_iter)
470 {
471  GtkTreeModel *model, *f_model, *s_model;
472  GtkTreeIter iter, f_iter;
473 
474  g_return_val_if_fail(GNC_IS_TREE_VIEW_PRICE(view), FALSE);
475  g_return_val_if_fail(price != NULL, FALSE);
476  g_return_val_if_fail(s_iter != NULL, FALSE);
477 
478  ENTER("view %p, price %p", view, price);
479 
480  /* Reach down to the real model and get an iter for this price */
481  s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
482  f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
483  model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
484  if (!gnc_tree_model_price_get_iter_from_price (GNC_TREE_MODEL_PRICE(model), price, &iter))
485  {
486  LEAVE("model_get_iter_from_price failed");
487  return FALSE;
488  }
489 
490  /* convert back to a sort iter */
491  gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER(f_model),
492  &f_iter, &iter);
493  gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model),
494  s_iter, &f_iter);
495  LEAVE(" ");
496  return TRUE;
497 }
498 #endif /* Not Used */
499 
500 /************************************************************/
501 /* Price Tree View Filter Functions */
502 /************************************************************/
503 
504 /************************************************************/
505 /* Price Tree View Visibility Filter */
506 /************************************************************/
507 
508 typedef struct
509 {
511  gnc_tree_view_price_cm_filter_func user_cm_fn;
512  gnc_tree_view_price_pc_filter_func user_pc_fn;
513  gpointer user_data;
514  GDestroyNotify user_destroy;
516 
517 static void
518 gnc_tree_view_price_filter_destroy (gpointer data)
519 {
520  filter_user_data *fd = data;
521 
522  if (fd->user_destroy)
523  fd->user_destroy(fd->user_data);
524  g_free(fd);
525 }
526 
527 static gboolean
528 gnc_tree_view_price_filter_helper (GtkTreeModel *model,
529  GtkTreeIter *iter,
530  gpointer data)
531 {
532  gnc_commodity_namespace *name_space;
533  gnc_commodity *commodity;
534  GNCPrice *price;
535  filter_user_data *fd = data;
536 
537  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
538  g_return_val_if_fail (iter != NULL, FALSE);
539 
540  if (gnc_tree_model_price_iter_is_namespace (GNC_TREE_MODEL_PRICE(model), iter))
541  {
542  if (fd->user_ns_fn)
543  {
544  name_space = gnc_tree_model_price_get_namespace (GNC_TREE_MODEL_PRICE(model), iter);
545  return fd->user_ns_fn(name_space, fd->user_data);
546  }
547  return TRUE;
548  }
549 
550  if (gnc_tree_model_price_iter_is_commodity (GNC_TREE_MODEL_PRICE(model), iter))
551  {
552  if (fd->user_cm_fn)
553  {
554  commodity = gnc_tree_model_price_get_commodity (GNC_TREE_MODEL_PRICE(model), iter);
555  return fd->user_cm_fn(commodity, fd->user_data);
556  }
557  return TRUE;
558  }
559 
560  if (gnc_tree_model_price_iter_is_price (GNC_TREE_MODEL_PRICE(model), iter))
561  {
562  if (fd->user_pc_fn)
563  {
564  price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model), iter);
565  return fd->user_pc_fn(price, fd->user_data);
566  }
567  return TRUE;
568  }
569 
570  return FALSE;
571 }
572 
573 /*
574  * Set an GtkTreeModel visible filter on this price. This filter will be
575  * called for each price that the tree is about to show, and the
576  * price will be passed to the callback function.
577  */
578 void
579 gnc_tree_view_price_set_filter (GncTreeViewPrice *view,
581  gnc_tree_view_price_cm_filter_func cm_func,
582  gnc_tree_view_price_pc_filter_func pc_func,
583  gpointer data,
584  GDestroyNotify destroy)
585 {
586  GtkTreeModel *f_model, *s_model;
587  filter_user_data *fd = data;
588 
589  ENTER("view %p, ns func %p, cm func %p, pc func %p, data %p, destroy %p",
590  view, ns_func, cm_func, pc_func, data, destroy);
591 
592  g_return_if_fail(GNC_IS_TREE_VIEW_PRICE(view));
593  g_return_if_fail((ns_func != NULL) || (cm_func != NULL));
594 
595  fd = g_malloc(sizeof(filter_user_data));
596  fd->user_ns_fn = ns_func;
597  fd->user_cm_fn = cm_func;
598  fd->user_pc_fn = pc_func;
599  fd->user_data = data;
600  fd->user_destroy = destroy;
601 
602  s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
603  f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
604 
605  /* disconnect model from view */
606  g_object_ref (G_OBJECT(s_model));
607  gtk_tree_view_set_model (GTK_TREE_VIEW(view), NULL);
608 
609  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model),
610  gnc_tree_view_price_filter_helper,
611  fd,
612  gnc_tree_view_price_filter_destroy);
613 
614  /* Whack any existing levels. The top two levels have been created
615  * before this routine can be called. Unfortunately, if the just
616  * applied filter filters out all the nodes in the tree, the gtk
617  * code throws a critical error. This occurs when there are no
618  * prices in the price database. Once the very first price has been
619  * added this error message goes away. */
620  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model));
621 
622  /* connect model to view */
623  gtk_tree_view_set_model (GTK_TREE_VIEW(view), s_model);
624  g_object_unref (G_OBJECT(s_model));
625 
626  LEAVE(" ");
627 }
628 
629 /************************************************************/
630 /* Price Tree View Get/Set Functions */
631 /************************************************************/
632 
633 /*
634  * Retrieve the selected price from an price tree view. The
635  * price tree must be in single selection mode.
636  */
637 GNCPrice *
639 {
640  GtkTreeSelection *selection;
641  GtkTreeModel *model, *f_model, *s_model;
642  GtkTreeIter iter, f_iter, s_iter;
643  GNCPrice *price;
644 
645  ENTER("view %p", view);
646  g_return_val_if_fail (GNC_IS_TREE_VIEW_PRICE (view), NULL);
647 
648  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
649  if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter))
650  {
651  LEAVE("no price, get_selected failed");
652  return FALSE;
653  }
654 
655  gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
656  &f_iter, &s_iter);
657 
658  f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
659  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
660  &iter, &f_iter);
661 
662  model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
663  price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model),
664  &iter);
665  LEAVE("price %p", price);
666  return price;
667 }
668 
669 /*
670  * Selects a single price in the price tree view. The price
671  * tree must be in single selection mode.
672  */
673 void
675  GNCPrice *price)
676 {
677  GtkTreeModel *model, *f_model, *s_model;
678  GtkTreePath *path, *f_path, *s_path, *parent_path;
679  GtkTreeSelection *selection;
680 
681  ENTER("view %p, price %p", view, price);
682 
683  /* Clear any existing selection. */
684  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
685  gtk_tree_selection_unselect_all (selection);
686 
687  if (price == NULL)
688  return;
689 
690  s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
691  f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
692  model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
693 
694  path = gnc_tree_model_price_get_path_from_price (GNC_TREE_MODEL_PRICE(model), price);
695  if (path == NULL)
696  {
697  LEAVE("get_path_from_price failed");
698  return;
699  }
700  debug_path(DEBUG, path);
701 
702  f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model),
703  path);
704  gtk_tree_path_free(path);
705  if (f_path == NULL)
706  {
707  LEAVE("no filter path");
708  return;
709  }
710  debug_path(DEBUG, f_path);
711 
712  s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
713  f_path);
714  gtk_tree_path_free(f_path);
715  if (s_path == NULL)
716  {
717  LEAVE("no sort path");
718  return;
719  }
720 
721  /* gtk_tree_view requires that a row be visible before it can be selected */
722  parent_path = gtk_tree_path_copy (s_path);
723  if (gtk_tree_path_up (parent_path))
724  {
725  /* This function is misnamed. It expands the actual item
726  * specified, not the path to the item specified. I.E. It expands
727  * one level too many, thus the get of the parent. */
728  gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
729  }
730  gtk_tree_path_free(parent_path);
731 
732  gtk_tree_selection_select_path (selection, s_path);
733  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
734  debug_path(LEAVE, s_path);
735  gtk_tree_path_free(s_path);
736 }
737 
738 /*
739  * This helper function is called once for each row in the tree view
740  * that is currently selected. Its task is to add the corresponding
741  * price to the end of a glist.
742  */
743 static void
744 get_selected_prices_helper (GtkTreeModel *s_model,
745  GtkTreePath *s_path,
746  GtkTreeIter *s_iter,
747  gpointer data)
748 {
749  GList **return_list = data;
750  GtkTreeModel *model, *f_model;
751  GtkTreeIter iter, f_iter;
752  GNCPrice *price;
753 
754  gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
755  &f_iter, s_iter);
756 
757  f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
758  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
759  &iter, &f_iter);
760 
761  model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
762  price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model),
763  &iter);
764  if (price)
765  *return_list = g_list_prepend (*return_list, price);
766 }
767 
768 /*
769  * Given a price tree view, return a list of the selected prices. The
770  * price tree must be in multiple selection mode.
771  *
772  * Note: It is the responsibility of the caller to free the returned
773  * list.
774  */
775 GList *
777 {
778  GtkTreeSelection *selection;
779  GList *return_list = NULL;
780 
781  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
782  gtk_tree_selection_selected_foreach(selection, get_selected_prices_helper, &return_list);
783  return g_list_reverse (return_list);
784 }
785 
786 static void
787 get_selected_commodity_helper (GtkTreeModel *s_model,
788  GtkTreePath *s_path,
789  GtkTreeIter *s_iter,
790  gpointer data)
791 {
792  GList **return_list = data;
793  GtkTreeModel *model, *f_model;
794  GtkTreeIter iter, f_iter;
795  gnc_commodity *commodity;
796 
797  gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
798  &f_iter, s_iter);
799 
800  f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
801  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
802  &iter, &f_iter);
803 
804  model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
805  commodity = gnc_tree_model_price_get_commodity (GNC_TREE_MODEL_PRICE(model), &iter);
806 
807  if (commodity)
808  *return_list = g_list_prepend (*return_list, commodity);
809 }
810 
811 /*
812  * Given a price tree view, return a list of the selected rows that have
813  * commodities but are not prices, the parent rows for prices. The
814  * price tree must be in multiple selection mode.
815  *
816  * Note: It is the responsibility of the caller to free the returned
817  * list.
818  */
819 GList *
821 {
822  GtkTreeSelection *selection;
823  GList *return_list = NULL;
824 
825  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
826  gtk_tree_selection_selected_foreach (selection, get_selected_commodity_helper, &return_list);
827  return g_list_reverse (return_list);
828 }
gboolean gnc_tree_model_price_get_iter_from_price(GncTreeModelPrice *model, GNCPrice *price, GtkTreeIter *iter)
Convert a price pointer into a GtkTreeIter.
gnc_commodity * gnc_tree_model_price_get_commodity(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash commodity.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
a simple price database for gnucash
const char * gnc_commodity_namespace_get_gui_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure in a form suitable to present to the user...
utility functions for the GnuCash UI
int safe_utf8_collate(const char *da, const char *db)
Collate two UTF-8 strings.
GtkTreeModel implementation for gnucash price database.
gboolean(* gnc_tree_view_price_ns_filter_func)(gnc_commodity_namespace *, gpointer data)
This function attaches a filter function to the given price tree.
common utilities for manipulating a GtkTreeView within gnucash
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
GNCPrice * gnc_tree_model_price_get_price(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash price.
GNCPrice * gnc_tree_view_price_get_selected_price(GncTreeViewPrice *view)
This function returns the price associated with the selected item in the price tree view...
GList * gnc_tree_view_price_get_selected_commodities(GncTreeViewPrice *view)
This function returns a list of commodities associated with the selected rows that are not prices but...
GList * gnc_tree_view_price_get_selected_prices(GncTreeViewPrice *view)
This function returns a list of the prices associated with the selected items in the price tree view...
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
gnc_commodity_namespace * gnc_tree_model_price_get_namespace(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash commodity namespace.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
GtkTreeViewColumn * gnc_tree_view_add_numeric_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *sizing_text, gint model_data_column, gint model_color_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new numeric column to a GncTreeView base view.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
Definition: gnc-pricedb.c:967
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Definition: gnc-ui-util.c:1197
gboolean gnc_tree_model_price_iter_is_namespace(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a "commodity namespace".
GtkTreeView implementation for gnucash price tree.
gboolean gnc_tree_model_price_iter_is_commodity(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a commodity.
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting, their "always" visibility setting, and the last saved state if available.
Gnome specific utility functions.
gboolean gnc_tree_model_price_iter_is_price(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a price.
All type declarations for the whole Gnucash engine.
void gnc_tree_view_price_set_selected_price(GncTreeViewPrice *view, GNCPrice *price)
This function selects an price in the price tree view.
GLib helper routines.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the &#39;print&#39; name for the specified commodity.
GtkTreeModel * gnc_tree_model_price_new(QofBook *book, GNCPriceDB *price_db)
Create a new GtkTreeModel for manipulating gnucash commodity prices.
#define gnc_leave_return_if_fail(test)
Replacement for g_return_if_fail, but calls LEAVE if the test fails.
Definition: qoflog.h:300
GtkTreePath * gnc_tree_model_price_get_path_from_price(GncTreeModelPrice *model, GNCPrice *price)
Convert a price pointer into a GtkTreePath.
GtkTreeViewColumn * gnc_tree_view_add_text_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *icon_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new text column to a GncTreeView base view.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
GtkTreeView * gnc_tree_view_price_new(QofBook *book, const gchar *first_property_name,...)
Create a new price tree view.
The instance data structure for a price tree model.