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