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