GnuCash  4.11-137-g155922540d+
gnc-tree-model-price.c
1 /*
2  * gnc-tree-model-price.c -- GtkTreeModel implementation to display
3  * prices in a GtkTreeView.
4  *
5  * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
6  * Copyright (C) 2003 David Hampton <hampton@employees.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, contact:
20  *
21  * Free Software Foundation Voice: +1-617-542-5942
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
23  * Boston, MA 02110-1301, USA gnu@gnu.org
24  */
25 
26 /*
27  * In this model, valid paths take the form "X", "X:Y", or "X:Y:Z", where:
28  * X is an index into the namespaces list held by the commodity db
29  * Y is an index into the commodity list for the namespace
30  * Z is an index into the price list for the commodity
31  *
32  * Iterators are populated with the following private data:
33  * iter->user_data Type NAMESPACE | COMMODITY | PRICE
34  * iter->user_data2 A pointer to the namespace|commodity|item structure
35  * iter->user_data3 The index of the item within its parent list
36  */
37 
38 #include <config.h>
39 
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42 #include <string.h>
43 
44 #include "gnc-component-manager.h"
45 #include "gnc-engine.h"
46 #include "gnc-gobject-utils.h"
47 #include "gnc-pricedb.h"
48 #include "gnc-tree-model-price.h"
49 #include "gnc-ui-util.h"
50 
51 #define ITER_IS_NAMESPACE GINT_TO_POINTER(1)
52 #define ITER_IS_COMMODITY GINT_TO_POINTER(2)
53 #define ITER_IS_PRICE GINT_TO_POINTER(3)
54 
55 /*
56  * There's a race condition in this code where a redraw of the tree
57  * view widget gets in between the two phases of removing a GNCPrice
58  * from the model. I tried bumping the priority of the idle function
59  * by 100, which should have put it well above the priority of GTK's
60  * redraw function, but the race condition persisted. I also tried
61  * eliminating the second phase of the removal, but that screws up the
62  * view filter (which lives above this code and therefore there's no
63  * way to access it) and causes other problems. The workaround is to
64  * accept a tree path that points to a NULL price, and simply return
65  * the null string to be printed by the view. The removal kicks in
66  * immediately after the redraw and causes the blank line to be
67  * removed.
68  *
69  * Charles Day: I found that by the time the main loop is reached and
70  * the idle timer goes off, many qof events may have been generated and
71  * handled. In particular, a commodity could be removed, edited, and
72  * re-added by the security editor and all those events would happen
73  * before the timer goes off. This caused a problem where a re-added
74  * commodity would get whacked when the timer went off. I found that
75  * adding a check for pending removals at the beginning of the event
76  * handler fixes that problem and also resolves the race condition.
77  *
78  */
79 #define RACE_CONDITION_SOLVED
80 
82 static QofLogModule log_module = GNC_MOD_GUI;
83 
85 static void gnc_tree_model_price_class_init (GncTreeModelPriceClass *klass);
86 static void gnc_tree_model_price_init (GncTreeModelPrice *model);
87 static void gnc_tree_model_price_finalize (GObject *object);
88 static void gnc_tree_model_price_dispose (GObject *object);
89 
90 static void gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface);
91 static guint gnc_tree_model_price_get_flags (GtkTreeModel *tree_model);
92 static int gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model);
93 static GType gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
94  int index);
95 static gboolean gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
96  GtkTreeIter *iter,
97  GtkTreePath *path);
98 static GtkTreePath *gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
99  GtkTreeIter *iter);
100 static void gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
101  GtkTreeIter *iter,
102  int column,
103  GValue *value);
104 static gboolean gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
105  GtkTreeIter *iter);
106 static gboolean gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
107  GtkTreeIter *iter,
108  GtkTreeIter *parent);
109 static gboolean gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
110  GtkTreeIter *iter);
111 static int gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
112  GtkTreeIter *iter);
113 static gboolean gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
114  GtkTreeIter *iter,
115  GtkTreeIter *parent,
116  int n);
117 static gboolean gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
118  GtkTreeIter *iter,
119  GtkTreeIter *child);
120 static void gnc_tree_model_price_event_handler (QofInstance *entity,
121  QofEventId event_type,
122  gpointer user_data,
123  gpointer event_data);
124 
127 {
128  QofBook *book;
129  GNCPriceDB *price_db;
130  gint event_handler_id;
131  GNCPrintAmountInfo print_info;
133 
134 #define GNC_TREE_MODEL_PRICE_GET_PRIVATE(o) \
135  ((GncTreeModelPricePrivate*)gnc_tree_model_price_get_instance_private((GncTreeModelPrice*)o))
136 
138 static GObjectClass *parent_class = NULL;
139 
140 G_DEFINE_TYPE_WITH_CODE(GncTreeModelPrice, gnc_tree_model_price, GNC_TYPE_TREE_MODEL,
141  G_ADD_PRIVATE(GncTreeModelPrice)
142  G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL,
143  gnc_tree_model_price_tree_model_init))
144 
145 static void
146 gnc_tree_model_price_class_init (GncTreeModelPriceClass *klass)
147 {
148  GObjectClass *o_class = G_OBJECT_CLASS (klass);
149 
150  parent_class = g_type_class_peek_parent (klass);
151 
152  o_class->finalize = gnc_tree_model_price_finalize;
153  o_class->dispose = gnc_tree_model_price_dispose;
154 }
155 
156 static void
157 gnc_tree_model_price_init (GncTreeModelPrice *model)
158 {
160 
161  while (model->stamp == 0)
162  {
163  model->stamp = g_random_int ();
164  }
165 
166  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
167  priv->print_info = gnc_default_price_print_info(NULL);
168 }
169 
170 static void
171 gnc_tree_model_price_finalize (GObject *object)
172 {
173  GncTreeModelPrice *model;
175 
176  ENTER("model %p", object);
177 
178  g_return_if_fail (object != NULL);
179  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (object));
180 
181  model = GNC_TREE_MODEL_PRICE (object);
182  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
183 
184  priv->book = NULL;
185  priv->price_db = NULL;
186 
187  G_OBJECT_CLASS (parent_class)->finalize (object);
188  LEAVE(" ");
189 }
190 
191 static void
192 gnc_tree_model_price_dispose (GObject *object)
193 {
194  GncTreeModelPrice *model;
196 
197  ENTER("model %p", object);
198  g_return_if_fail (object != NULL);
199  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (object));
200 
201  model = GNC_TREE_MODEL_PRICE (object);
202  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
203 
204  if (priv->event_handler_id)
205  {
206  qof_event_unregister_handler (priv->event_handler_id);
207  priv->event_handler_id = 0;
208  }
209 
210  if (G_OBJECT_CLASS (parent_class)->dispose)
211  G_OBJECT_CLASS (parent_class)->dispose (object);
212  LEAVE(" ");
213 }
214 
215 GtkTreeModel *
216 gnc_tree_model_price_new (QofBook *book, GNCPriceDB *price_db)
217 {
218  GncTreeModelPrice *model;
220  const GList *item;
221 
222  ENTER(" ");
223 
224  item = gnc_gobject_tracking_get_list(GNC_TREE_MODEL_PRICE_NAME);
225  for ( ; item; item = g_list_next(item))
226  {
227  model = (GncTreeModelPrice *)item->data;
228  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
229  if (priv->price_db == price_db)
230  {
231  g_object_ref(G_OBJECT(model));
232  LEAVE("returning existing model %p", model);
233  return GTK_TREE_MODEL(model);
234  }
235  }
236 
237  model = g_object_new (GNC_TYPE_TREE_MODEL_PRICE, NULL);
238 
239  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
240  priv->book = book;
241  priv->price_db = price_db;
242 
243  priv->event_handler_id =
244  qof_event_register_handler (gnc_tree_model_price_event_handler, model);
245 
246  LEAVE("returning new model %p", model);
247  return GTK_TREE_MODEL (model);
248 }
249 
250 gboolean
252  GtkTreeIter *iter)
253 {
254  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
255  g_return_val_if_fail (iter != NULL, FALSE);
256  g_return_val_if_fail (iter->user_data != NULL, FALSE);
257  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
258 
259  return (iter->user_data == ITER_IS_NAMESPACE);
260 }
261 
262 gboolean
264  GtkTreeIter *iter)
265 {
266  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
267  g_return_val_if_fail (iter != NULL, FALSE);
268  g_return_val_if_fail (iter->user_data != NULL, FALSE);
269  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
270 
271  return (iter->user_data == ITER_IS_COMMODITY);
272 }
273 
274 gboolean
276  GtkTreeIter *iter)
277 {
278  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
279  g_return_val_if_fail (iter != NULL, FALSE);
280  g_return_val_if_fail (iter->user_data != NULL, FALSE);
281  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
282 
283  return (iter->user_data == ITER_IS_PRICE);
284 }
285 
286 gnc_commodity_namespace *
288  GtkTreeIter *iter)
289 {
290  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
291  g_return_val_if_fail (iter != NULL, NULL);
292  g_return_val_if_fail (iter->user_data != NULL, NULL);
293  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
294 
295  if (iter->user_data != ITER_IS_NAMESPACE)
296  return NULL;
297  return (gnc_commodity_namespace *)iter->user_data2;
298 }
299 
300 gnc_commodity *
302  GtkTreeIter *iter)
303 {
304  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
305  g_return_val_if_fail (iter != NULL, NULL);
306  g_return_val_if_fail (iter->user_data != NULL, NULL);
307  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
308 
309  if (iter->user_data != ITER_IS_COMMODITY)
310  return NULL;
311  return (gnc_commodity *)iter->user_data2;
312 }
313 
314 GNCPrice *
316  GtkTreeIter *iter)
317 {
318  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
319  g_return_val_if_fail (iter != NULL, NULL);
320  g_return_val_if_fail (iter->user_data != NULL, NULL);
321  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
322 
323  if (iter->user_data != ITER_IS_PRICE)
324  return NULL;
325  return (GNCPrice *)iter->user_data2;
326 }
327 
328 /************************************************************/
329 /* Gnc Tree Model Debugging Utility Function */
330 /************************************************************/
331 
332 #define debug_path(fn, path) { \
333  gchar *path_string = gtk_tree_path_to_string(path); \
334  fn("tree path %s", path_string? path_string : "(NULL)"); \
335  g_free(path_string); \
336  }
337 
338 #define ITER_STRING_LEN 256
339 
340 static const gchar *
341 iter_to_string (GncTreeModelPrice *model, GtkTreeIter *iter)
342 {
344  gnc_commodity_namespace *name_space;
345  gnc_commodity *commodity;
346  GNCPrice *price;
347 #ifdef G_THREADS_ENABLED
348  static GPrivate gtmits_buffer_key = G_PRIVATE_INIT(g_free);
349  gchar *string;
350 
351  string = g_private_get (&gtmits_buffer_key);
352  if (string == NULL)
353  {
354  string = g_malloc(ITER_STRING_LEN + 1);
355  g_private_set (&gtmits_buffer_key, string);
356  }
357 #else
358  static char string[ITER_STRING_LEN + 1];
359 #endif
360 
361  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
362  if (iter)
363  {
364  switch (GPOINTER_TO_INT(iter->user_data))
365  {
366  case GPOINTER_TO_INT(ITER_IS_NAMESPACE):
367  name_space = (gnc_commodity_namespace *) iter->user_data2;
368  snprintf(string, ITER_STRING_LEN,
369  "[stamp:%x data:%d (NAMESPACE), %p (%s), %d]",
370  iter->stamp, GPOINTER_TO_INT(iter->user_data),
371  iter->user_data2, gnc_commodity_namespace_get_name (name_space),
372  GPOINTER_TO_INT(iter->user_data3));
373  break;
374 
375  case GPOINTER_TO_INT(ITER_IS_COMMODITY):
376  commodity = (gnc_commodity *) iter->user_data2;
377  snprintf(string, ITER_STRING_LEN,
378  "[stamp:%x data:%d (COMMODITY), %p (%s), %d]",
379  iter->stamp, GPOINTER_TO_INT(iter->user_data),
380  iter->user_data2, gnc_commodity_get_mnemonic (commodity),
381  GPOINTER_TO_INT(iter->user_data3));
382  break;
383 
384  case GPOINTER_TO_INT(ITER_IS_PRICE):
385  price = (GNCPrice *) iter->user_data2;
386  commodity = gnc_price_get_commodity(price);
387  snprintf(string, ITER_STRING_LEN,
388  "[stamp:%x data:%d (PRICE), %p (%s:%s), %d]",
389  iter->stamp, GPOINTER_TO_INT(iter->user_data),
390  iter->user_data2, gnc_commodity_get_mnemonic (commodity),
391  xaccPrintAmount (gnc_price_get_value (price), priv->print_info),
392  GPOINTER_TO_INT(iter->user_data3));
393  break;
394 
395  default:
396  snprintf(string, ITER_STRING_LEN,
397  "[stamp:%x data:%d (UNKNOWN), %p, %d]",
398  iter->stamp,
399  GPOINTER_TO_INT(iter->user_data),
400  iter->user_data2,
401  GPOINTER_TO_INT(iter->user_data3));
402  break;
403  }
404  }
405  return string;
406 }
407 
408 
409 /************************************************************/
410 /* Gtk Tree Model Required Interface Functions */
411 /************************************************************/
412 
413 static void
414 gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface)
415 {
416  iface->get_flags = gnc_tree_model_price_get_flags;
417  iface->get_n_columns = gnc_tree_model_price_get_n_columns;
418  iface->get_column_type = gnc_tree_model_price_get_column_type;
419  iface->get_iter = gnc_tree_model_price_get_iter;
420  iface->get_path = gnc_tree_model_price_get_path;
421  iface->get_value = gnc_tree_model_price_get_value;
422  iface->iter_next = gnc_tree_model_price_iter_next;
423  iface->iter_children = gnc_tree_model_price_iter_children;
424  iface->iter_has_child = gnc_tree_model_price_iter_has_child;
425  iface->iter_n_children = gnc_tree_model_price_iter_n_children;
426  iface->iter_nth_child = gnc_tree_model_price_iter_nth_child;
427  iface->iter_parent = gnc_tree_model_price_iter_parent;
428 }
429 
430 static guint
431 gnc_tree_model_price_get_flags (GtkTreeModel *tree_model)
432 {
433  return 0;
434 }
435 
436 static int
437 gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model)
438 {
439  return GNC_TREE_MODEL_PRICE_NUM_COLUMNS;
440 }
441 
442 static GType
443 gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
444  int index)
445 {
446  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), G_TYPE_INVALID);
447  g_return_val_if_fail ((index < GNC_TREE_MODEL_PRICE_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID);
448 
449  switch (index)
450  {
451  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
452  case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
453  case GNC_TREE_MODEL_PRICE_COL_DATE:
454  case GNC_TREE_MODEL_PRICE_COL_SOURCE:
455  case GNC_TREE_MODEL_PRICE_COL_TYPE:
456  case GNC_TREE_MODEL_PRICE_COL_VALUE:
457  return G_TYPE_STRING;
458  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
459  return G_TYPE_BOOLEAN;
460  default:
461  g_assert_not_reached ();
462  return G_TYPE_INVALID;
463  }
464 }
465 
466 static gboolean
467 gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
468  GtkTreeIter *iter,
469  GtkTreePath *path)
470 {
471  GncTreeModelPrice *model;
473  gnc_commodity_table *ct;
474  gnc_commodity_namespace *name_space;
475  gnc_commodity *commodity = NULL;
476  GNCPrice *price;
477  GList *ns_list, *cm_list;
478  guint i, depth;
479 
480  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
481 
482  depth = gtk_tree_path_get_depth (path);
483  ENTER("model %p, iter %p, path %p (depth %d)", tree_model, iter, path, depth);
484  debug_path(DEBUG, path);
485 
486  /* Check the path depth. */
487  if (depth == 0)
488  {
489  LEAVE("depth too small");
490  return FALSE;
491  }
492  if (depth > 3)
493  {
494  LEAVE("depth too big");
495  return FALSE;
496  }
497 
498  /* Make sure the model has a price db. */
499  model = GNC_TREE_MODEL_PRICE (tree_model);
500  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
501  if (priv->price_db == NULL)
502  {
503  LEAVE("no price db");
504  return FALSE;
505  }
506 
507  /* Verify the first part of the path: the namespace. */
508  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
510  i = gtk_tree_path_get_indices (path)[0];
511  name_space = g_list_nth_data (ns_list, i);
512  if (!name_space)
513  {
514  LEAVE("invalid path at namespace");
515  return FALSE;
516  }
517 
518  if (depth == 1)
519  {
520  /* Return an iterator for the namespace. */
521  iter->stamp = model->stamp;
522  iter->user_data = ITER_IS_NAMESPACE;
523  iter->user_data2 = name_space;
524  iter->user_data3 = GINT_TO_POINTER(i);
525  LEAVE("iter (ns) %s", iter_to_string(model, iter));
526  return TRUE;
527  }
528 
529  /* Verify the second part of the path: the commodity. */
530  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
531  i = gtk_tree_path_get_indices (path)[1];
532  commodity = g_list_nth_data (cm_list, i);
533  if (!commodity)
534  {
535  LEAVE("invalid path at commodity");
536  return FALSE;
537  }
538 
539  if (depth == 2)
540  {
541  /* Return an iterator for the commodity. */
542  iter->stamp = model->stamp;
543  iter->user_data = ITER_IS_COMMODITY;
544  iter->user_data2 = commodity;
545  iter->user_data3 = GINT_TO_POINTER(i);
546  LEAVE("iter (cm) %s", iter_to_string(model, iter));
547  return TRUE;
548  }
549 
550  /* Verify the third part of the path: the price. */
551  i = gtk_tree_path_get_indices (path)[2];
552  price = gnc_pricedb_nth_price(priv->price_db, commodity, i);
553  /* There's a race condition here that I can't resolve.
554  * Comment this check out for now, and we'll handle the
555  * resulting problem elsewhere. */
556 #ifdef RACE_CONDITION_SOLVED
557  if (!price)
558  {
559  LEAVE("invalid path at price");
560  return FALSE;
561  }
562 #endif
563 
564  /* Return an iterator for the price. */
565  iter->stamp = model->stamp;
566  iter->user_data = ITER_IS_PRICE;
567  iter->user_data2 = price;
568  iter->user_data3 = GINT_TO_POINTER(i);
569  LEAVE("iter (pc) %s", iter_to_string(model, iter));
570  return TRUE;
571 }
572 
573 static GtkTreePath *
574 gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
575  GtkTreeIter *iter)
576 {
577  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
579  gnc_commodity_table *ct;
580  gnc_commodity_namespace *name_space;
581  gnc_commodity *commodity;
582  GList *ns_list, *cm_list;
583  GtkTreePath *path;
584 
585  ENTER("model %p, iter %p (%s)", tree_model, iter, iter_to_string(model, iter));
586  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
587  g_return_val_if_fail (iter != NULL, NULL);
588  g_return_val_if_fail (iter->user_data != NULL, NULL);
589  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
590 
591  /* Make sure this model has a price db. */
592  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
593  if (priv->price_db == NULL)
594  {
595  LEAVE("no price db");
596  return FALSE;
597  }
598 
599  if (iter->user_data == ITER_IS_NAMESPACE)
600  {
601  /* Create a path to the namespace. This is just the index into
602  * the namespace list, which we already stored in user_data3. */
603  path = gtk_tree_path_new ();
604  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
605  debug_path(LEAVE, path);
606  return path;
607  }
608 
609  /* Get the namespaces list. */
610  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
612 
613  if (iter->user_data == ITER_IS_COMMODITY)
614  {
615  /* Create a path to the commodity. */
616  commodity = (gnc_commodity*)iter->user_data2;
617  name_space = gnc_commodity_get_namespace_ds(commodity);
618  path = gtk_tree_path_new ();
619  gtk_tree_path_append_index (path, g_list_index (ns_list, name_space));
620  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
621  debug_path(LEAVE, path);
622  return path;
623  }
624 
625  /* Create a path to the price. */
626  commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
627  name_space = gnc_commodity_get_namespace_ds(commodity);
628  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
629  path = gtk_tree_path_new ();
630  gtk_tree_path_append_index (path, g_list_index (ns_list, name_space));
631  gtk_tree_path_append_index (path, g_list_index (cm_list, commodity));
632  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
633  debug_path(LEAVE, path);
634  return path;
635 }
636 
637 static void
638 gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
639  GtkTreeIter *iter,
640  int column,
641  GValue *value)
642 {
643  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
645  gnc_commodity_namespace *name_space;
646  gnc_commodity *commodity;
647  GNCPrice *price;
648  char datebuff[MAX_DATE_LENGTH + 1];
649  memset (datebuff, 0, sizeof(datebuff));
650 
651  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (model));
652  g_return_if_fail (iter != NULL);
653 #ifdef RACE_CONDITION_SOLVED
654  g_return_if_fail (iter->user_data != NULL);
655 #endif
656  g_return_if_fail (iter->stamp == model->stamp);
657 
658  if (iter->user_data == ITER_IS_NAMESPACE)
659  {
660  name_space = (gnc_commodity_namespace *)iter->user_data2;
661  switch (column)
662  {
663  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
664  g_value_init (value, G_TYPE_STRING);
665  g_value_set_string (value, gnc_commodity_namespace_get_gui_name (name_space));
666  break;
667  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
668  g_value_init (value, G_TYPE_BOOLEAN);
669  g_value_set_boolean (value, FALSE);
670  break;
671  default:
672  g_value_init (value, G_TYPE_STRING);
673  g_value_set_string (value, "");
674  break;
675  }
676  return;
677  }
678 
679  if (iter->user_data == ITER_IS_COMMODITY)
680  {
681  commodity = (gnc_commodity *)iter->user_data2;
682  switch (column)
683  {
684  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
685  g_value_init (value, G_TYPE_STRING);
686  g_value_set_string (value, gnc_commodity_get_printname (commodity));
687  break;
688  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
689  g_value_init (value, G_TYPE_BOOLEAN);
690  g_value_set_boolean (value, FALSE);
691  break;
692  default:
693  g_value_init (value, G_TYPE_STRING);
694  g_value_set_string (value, "");
695  break;
696  }
697  return;
698  }
699 
700  price = (GNCPrice *)iter->user_data2;
701 #ifdef RACE_CONDITION_SOLVED
702  g_return_if_fail (price != NULL);
703 #else
704  if (price == NULL)
705  {
706  switch (column)
707  {
708  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
709  g_value_init (value, G_TYPE_STRING);
710  g_value_set_string (value, "");
711  break;
712  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
713  g_value_init (value, G_TYPE_BOOLEAN);
714  g_value_set_boolean (value, FALSE);
715  break;
716  default:
717  g_value_init (value, G_TYPE_STRING);
718  g_value_set_string (value, "");
719  break;
720  }
721  return;
722  }
723 #endif
724 
725  switch (column)
726  {
727  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
728  g_value_init (value, G_TYPE_STRING);
729  commodity = gnc_price_get_commodity (price);
730  g_value_set_string (value, gnc_commodity_get_printname (commodity));
731  break;
732  case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
733  g_value_init (value, G_TYPE_STRING);
734  commodity = gnc_price_get_currency (price);
735  g_value_set_string (value, gnc_commodity_get_printname (commodity));
736  break;
737  case GNC_TREE_MODEL_PRICE_COL_DATE:
739  gnc_price_get_time64 (price));
740  g_value_init (value, G_TYPE_STRING);
741  g_value_set_string (value, datebuff);
742  break;
743  case GNC_TREE_MODEL_PRICE_COL_SOURCE:
744  g_value_init (value, G_TYPE_STRING);
745  g_value_set_string (value, gettext (gnc_price_get_source_string (price)));
746  break;
747  case GNC_TREE_MODEL_PRICE_COL_TYPE:
748  g_value_init (value, G_TYPE_STRING);
749  g_value_set_string (value, gnc_price_get_typestr (price));
750  break;
751  case GNC_TREE_MODEL_PRICE_COL_VALUE:
752  g_value_init (value, G_TYPE_STRING);
753  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
754  g_value_set_string (value, xaccPrintAmount (gnc_price_get_value (price),
755  priv->print_info));
756  break;
757  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
758  g_value_init (value, G_TYPE_BOOLEAN);
759  g_value_set_boolean (value, TRUE);
760  break;
761  default:
762  g_assert_not_reached ();
763  }
764 }
765 
766 static gboolean
767 gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
768  GtkTreeIter *iter)
769 {
770  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
772  gnc_commodity_table *ct;
773  gnc_commodity *commodity;
774  gnc_commodity_namespace *name_space;
775  GList *list;
776  gint n;
777 
778  ENTER("model %p, iter %p(%s)", tree_model, iter, iter_to_string(model, iter));
779  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
780  g_return_val_if_fail (iter != NULL, FALSE);
781  g_return_val_if_fail (iter->user_data != NULL, FALSE);
782  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
783 
784  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
785  if (iter->user_data == ITER_IS_NAMESPACE)
786  {
787  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
789  n = GPOINTER_TO_INT(iter->user_data3) + 1;
790  iter->user_data2 = g_list_nth_data(list, n);
791  if (iter->user_data2 == NULL)
792  {
793  LEAVE("no next iter");
794  return FALSE;
795  }
796  iter->user_data3 = GINT_TO_POINTER(n);
797  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
798  return TRUE;
799  }
800  else if (iter->user_data == ITER_IS_COMMODITY)
801  {
802  name_space = gnc_commodity_get_namespace_ds((gnc_commodity *)iter->user_data2);
804  n = GPOINTER_TO_INT(iter->user_data3) + 1;
805  iter->user_data2 = g_list_nth_data(list, n);
806  if (iter->user_data2 == NULL)
807  {
808  LEAVE("no next iter");
809  return FALSE;
810  }
811  iter->user_data3 = GINT_TO_POINTER(n);
812  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
813  return TRUE;
814  }
815  else if (iter->user_data == ITER_IS_PRICE)
816  {
817  commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
818  n = GPOINTER_TO_INT(iter->user_data3) + 1;
819  iter->user_data2 = gnc_pricedb_nth_price(priv->price_db, commodity, n);
820  if (iter->user_data2 == NULL)
821  {
822  LEAVE("no next iter");
823  return FALSE;
824  }
825  iter->user_data3 = GINT_TO_POINTER(n);
826  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
827  return TRUE;
828  }
829  else
830  {
831  LEAVE("unknown iter type");
832  return FALSE;
833  }
834 }
835 
836 static gboolean
837 gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
838  GtkTreeIter *iter,
839  GtkTreeIter *parent)
840 {
841  GncTreeModelPrice *model;
843  gnc_commodity_table *ct;
844  gnc_commodity_namespace *name_space;
845  gnc_commodity *commodity;
846  GList *list;
847 
848  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
849 
850  model = GNC_TREE_MODEL_PRICE (tree_model);
851  ENTER("model %p, iter %p, parent %p (%s)",
852  tree_model, iter, parent, iter_to_string(model, parent));
853 
854  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
855  if (parent == NULL)
856  {
857  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
859  if (list == NULL)
860  {
861  LEAVE("no namespaces");
862  return FALSE;
863  }
864 
865  iter->stamp = model->stamp;
866  iter->user_data = ITER_IS_NAMESPACE;
867  iter->user_data2 = g_list_nth_data(list, 0);
868  iter->user_data3 = GINT_TO_POINTER(0);
869  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
870  return TRUE;
871  }
872 
873  if (parent->user_data == ITER_IS_NAMESPACE)
874  {
875  name_space = (gnc_commodity_namespace *)parent->user_data2;
877  if (list == NULL)
878  {
879  LEAVE("no commodities");
880  return FALSE;
881  }
882 
883  iter->stamp = model->stamp;
884  iter->user_data = ITER_IS_COMMODITY;
885  iter->user_data2 = g_list_nth_data(list, 0);
886  iter->user_data3 = GINT_TO_POINTER(0);
887  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
888  return TRUE;
889  }
890 
891  if (parent->user_data == ITER_IS_COMMODITY)
892  {
893  GNCPrice *price;
894  commodity = (gnc_commodity *)parent->user_data2;
895  price = gnc_pricedb_nth_price(priv->price_db, commodity, 0);
896  if (price == NULL)
897  {
898  LEAVE("no prices");
899  return FALSE;
900  }
901  iter->stamp = model->stamp;
902  iter->user_data = ITER_IS_PRICE;
903  iter->user_data2 = price;
904  iter->user_data3 = GINT_TO_POINTER(0);
905  LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
906  return TRUE;
907  }
908 
909  LEAVE("FALSE");
910  return FALSE;
911 }
912 
913 static gboolean
914 gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
915  GtkTreeIter *iter)
916 {
917  GncTreeModelPrice *model;
919  gnc_commodity_namespace *name_space;
920  gnc_commodity *commodity;
921  gboolean result;
922  GList *list;
923 
924  model = GNC_TREE_MODEL_PRICE (tree_model);
925  ENTER("model %p, iter %p (%s)", tree_model,
926  iter, iter_to_string(model, iter));
927  g_return_val_if_fail (tree_model != NULL, FALSE);
928  g_return_val_if_fail (iter != NULL, FALSE);
929 
930  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
931  if (iter->user_data == ITER_IS_PRICE)
932  {
933  LEAVE("price has no children");
934  return FALSE;
935  }
936 
937  if (iter->user_data == ITER_IS_NAMESPACE)
938  {
939  name_space = (gnc_commodity_namespace *)iter->user_data2;
941  LEAVE("%s children", list ? "has" : "no");
942  return list != NULL;
943  }
944 
945  if (iter->user_data == ITER_IS_COMMODITY)
946  {
947  commodity = (gnc_commodity *)iter->user_data2;
948  result = gnc_pricedb_has_prices(priv->price_db, commodity, NULL);
949  LEAVE("%s children", result ? "has" : "no");
950  return result;
951  }
952 
953  LEAVE("no children (unknown type)");
954  return FALSE;
955 }
956 
957 static int
958 gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
959  GtkTreeIter *iter)
960 {
961  GncTreeModelPrice *model;
963  gnc_commodity_table *ct;
964  gnc_commodity_namespace *name_space;
965  gnc_commodity *commodity;
966  GList *list;
967  gint n;
968 
969  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), -1);
970 
971  model = GNC_TREE_MODEL_PRICE (tree_model);
972  ENTER("model %p, iter %p (%s)", tree_model, iter,
973  iter_to_string(model, iter));
974 
975  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
976  if (iter == NULL)
977  {
978  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
980  LEAVE("ns list length %d", g_list_length(list));
981  return g_list_length (list);
982  }
983 
984  if (iter->user_data == ITER_IS_NAMESPACE)
985  {
986  name_space = (gnc_commodity_namespace *)iter->user_data2;
988  LEAVE("cm list length %d", g_list_length(list));
989  return g_list_length (list);
990  }
991 
992  if (iter->user_data == ITER_IS_COMMODITY)
993  {
994  commodity = (gnc_commodity *)iter->user_data2;
995  n = gnc_pricedb_num_prices(priv->price_db, commodity);
996  LEAVE("price list length %d", n);
997  return n;
998  }
999 
1000  LEAVE("0");
1001  return 0;
1002 }
1003 
1004 static gboolean
1005 gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
1006  GtkTreeIter *iter,
1007  GtkTreeIter *parent,
1008  int n)
1009 {
1010  GncTreeModelPrice *model;
1012  gnc_commodity_table *ct;
1013  gnc_commodity_namespace *name_space;
1014  gnc_commodity *commodity;
1015  GList *list;
1016 
1017  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
1018  g_return_val_if_fail (iter != NULL, FALSE);
1019 
1020  model = GNC_TREE_MODEL_PRICE (tree_model);
1021  ENTER("model %p, iter %p, parent %p (%s), n %d",
1022  tree_model, iter, parent, iter_to_string(model, parent), n);
1023 
1024  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
1025  if (parent == NULL)
1026  {
1027  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
1029 
1030  iter->stamp = model->stamp;
1031  iter->user_data = ITER_IS_NAMESPACE;
1032  iter->user_data2 = g_list_nth_data(list, n);
1033  iter->user_data3 = GINT_TO_POINTER(n);
1034  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
1035  return iter->user_data2 != NULL;
1036  }
1037 
1038  if (parent->user_data == ITER_IS_NAMESPACE)
1039  {
1040  name_space = (gnc_commodity_namespace *)parent->user_data2;
1041  list = gnc_commodity_namespace_get_commodity_list(name_space);
1042 
1043  iter->stamp = model->stamp;
1044  iter->user_data = ITER_IS_COMMODITY;
1045  iter->user_data2 = g_list_nth_data(list, n);
1046  iter->user_data3 = GINT_TO_POINTER(n);
1047  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
1048  return iter->user_data2 != NULL;
1049  }
1050 
1051  if (parent->user_data == ITER_IS_COMMODITY)
1052  {
1053  commodity = (gnc_commodity *)parent->user_data2;
1054 
1055  iter->stamp = model->stamp;
1056  iter->user_data = ITER_IS_PRICE;
1057  iter->user_data2 = gnc_pricedb_nth_price(priv->price_db, commodity, n);
1058  iter->user_data3 = GINT_TO_POINTER(n);
1059  LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
1060  return iter->user_data2 != NULL;
1061  }
1062 
1063  iter->stamp = 0;
1064  LEAVE("FALSE");
1065  return FALSE;
1066 }
1067 
1068 static gboolean
1069 gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
1070  GtkTreeIter *iter,
1071  GtkTreeIter *child)
1072 {
1073  GncTreeModelPrice *model;
1075  gnc_commodity_table *ct;
1076  gnc_commodity * commodity;
1077  gnc_commodity_namespace *name_space;
1078  GList *list;
1079 
1080  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
1081  g_return_val_if_fail (iter != NULL, FALSE);
1082  g_return_val_if_fail (child != NULL, FALSE);
1083 
1084  model = GNC_TREE_MODEL_PRICE (tree_model);
1085  ENTER("model %p, iter %p, child %p (%s)",
1086  tree_model, iter, child, iter_to_string(model, child));
1087 
1088  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
1089  if (child->user_data == ITER_IS_NAMESPACE)
1090  {
1091  LEAVE("ns has no parent");
1092  return FALSE;
1093  }
1094 
1095  if (child->user_data == ITER_IS_COMMODITY)
1096  {
1097  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
1099  name_space = gnc_commodity_get_namespace_ds((gnc_commodity*)child->user_data2);
1100 
1101  iter->stamp = model->stamp;
1102  iter->user_data = ITER_IS_NAMESPACE;
1103  iter->user_data2 = name_space;
1104  iter->user_data3 = GINT_TO_POINTER(g_list_index(list, name_space));
1105  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
1106  return TRUE;
1107  }
1108 
1109  commodity = gnc_price_get_commodity ((GNCPrice*)child->user_data2);
1110  name_space = gnc_commodity_get_namespace_ds(commodity);
1111  list = gnc_commodity_namespace_get_commodity_list(name_space);
1112 
1113  iter->stamp = model->stamp;
1114  iter->user_data = ITER_IS_COMMODITY;
1115  iter->user_data2 = commodity;
1116  iter->user_data3 = GINT_TO_POINTER(g_list_index(list, commodity));
1117  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
1118  return TRUE;
1119 }
1120 
1121 /************************************************************/
1122 /* Price Tree View Functions */
1123 /************************************************************/
1124 
1125 /*
1126  * Convert a model/price pair into a gtk_tree_model_iter. This
1127  * routine should only be called from the file
1128  * gnc-tree-view-price.c.
1129  */
1130 gboolean
1132  GNCPrice *price,
1133  GtkTreeIter *iter)
1134 {
1136  gnc_commodity *commodity;
1137  GList *list;
1138  gint n;
1139 
1140  ENTER("model %p, price %p, iter %p", model, price, iter);
1141  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1142  g_return_val_if_fail ((price != NULL), FALSE);
1143  g_return_val_if_fail ((iter != NULL), FALSE);
1144 
1145  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
1146  commodity = gnc_price_get_commodity(price);
1147  if (commodity == NULL)
1148  {
1149  LEAVE("no commodity");
1150  return FALSE;
1151  }
1152 
1153  list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
1154  if (list == NULL)
1155  {
1156  LEAVE("empty list");
1157  return FALSE;
1158  }
1159 
1160  n = g_list_index(list, price);
1161  if (n == -1)
1162  {
1163  gnc_price_list_destroy(list);
1164  LEAVE("not in list");
1165  return FALSE;
1166  }
1167 
1168  iter->stamp = model->stamp;
1169  iter->user_data = ITER_IS_PRICE;
1170  iter->user_data2 = price;
1171  iter->user_data3 = GINT_TO_POINTER(n);
1172  gnc_price_list_destroy(list);
1173  LEAVE("iter %s", iter_to_string(model, iter));
1174  return TRUE;
1175 }
1176 
1177 /*
1178  * Convert a model/price pair into a gtk_tree_model_path. This
1179  * routine should only be called from the file
1180  * gnc-tree-view-price.c.
1181  */
1182 GtkTreePath *
1184  GNCPrice *price)
1185 {
1186  GtkTreeIter tree_iter;
1187  GtkTreePath *tree_path;
1188 
1189  ENTER("model %p, price %p", model, price);
1190  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
1191  g_return_val_if_fail (price != NULL, NULL);
1192 
1193  if (!gnc_tree_model_price_get_iter_from_price (model, price, &tree_iter))
1194  {
1195  LEAVE("no iter");
1196  return NULL;
1197  }
1198 
1199  tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
1200  if (tree_path)
1201  {
1202  gchar *path_string = gtk_tree_path_to_string(tree_path);
1203  LEAVE("path (2) %s", path_string);
1204  g_free(path_string);
1205  }
1206  else
1207  {
1208  LEAVE("no path");
1209  }
1210  return tree_path;
1211 }
1212 
1213 /*
1214  * Convert a model/commodity pair into a gtk_tree_model_iter. This
1215  * routine should only be called from the file
1216  * gnc-tree-view-price.c.
1217  */
1218 gboolean
1220  gnc_commodity *commodity,
1221  GtkTreeIter *iter)
1222 {
1223  gnc_commodity_namespace *name_space;
1224  GList *list;
1225  gint n;
1226 
1227  ENTER("model %p, commodity %p, iter %p", model, commodity, iter);
1228  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1229  g_return_val_if_fail ((commodity != NULL), FALSE);
1230  g_return_val_if_fail ((iter != NULL), FALSE);
1231 
1232  name_space = gnc_commodity_get_namespace_ds(commodity);
1233  if (name_space == NULL)
1234  {
1235  LEAVE("no namespace");
1236  return FALSE;
1237  }
1238 
1239  list = gnc_commodity_namespace_get_commodity_list(name_space);
1240  if (list == NULL)
1241  {
1242  LEAVE("empty list");
1243  return FALSE;
1244  }
1245 
1246  n = g_list_index(list, commodity);
1247  if (n == -1)
1248  {
1249  LEAVE("commodity not in list");
1250  return FALSE;
1251  }
1252 
1253  iter->stamp = model->stamp;
1254  iter->user_data = ITER_IS_COMMODITY;
1255  iter->user_data2 = commodity;
1256  iter->user_data3 = GINT_TO_POINTER(n);
1257  LEAVE("iter %s", iter_to_string(model, iter));
1258  return TRUE;
1259 }
1260 
1261 /*
1262  * Convert a model/namespace pair into a gtk_tree_model_iter. This
1263  * routine should only be called from the file
1264  * gnc-tree-view-price.c.
1265  */
1266 gboolean
1268  gnc_commodity_namespace *name_space,
1269  GtkTreeIter *iter)
1270 {
1272  gnc_commodity_table *ct;
1273  GList *list;
1274  gint n;
1275 
1276  ENTER("model %p, namespace %p, iter %p", model, name_space, iter);
1277  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1278  g_return_val_if_fail ((name_space != NULL), FALSE);
1279  g_return_val_if_fail ((iter != NULL), FALSE);
1280 
1281  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
1282  ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
1284  if (list == NULL)
1285  {
1286  LEAVE("namespace list empty");
1287  return FALSE;
1288  }
1289 
1290  n = g_list_index(list, name_space);
1291  if (n == -1)
1292  {
1293  LEAVE("namespace not found");
1294  return FALSE;
1295  }
1296 
1297  iter->stamp = model->stamp;
1298  iter->user_data = ITER_IS_NAMESPACE;
1299  iter->user_data2 = name_space;
1300  iter->user_data3 = GINT_TO_POINTER(n);
1301  LEAVE("iter %s", iter_to_string(model, iter));
1302  return TRUE;
1303 }
1304 
1305 /************************************************************/
1306 /* Price Tree Model - Engine Event Handling Functions */
1307 /************************************************************/
1308 
1309 typedef struct _remove_data
1310 {
1311  GncTreeModelPrice *model;
1312  GtkTreePath *path;
1313 } remove_data;
1314 
1315 static GSList *pending_removals = NULL;
1316 
1328 static void
1329 gnc_tree_model_price_row_add (GncTreeModelPrice *model,
1330  GtkTreeIter *iter)
1331 {
1332  GtkTreePath *path;
1333  GtkTreeModel *tree_model;
1334  GtkTreeIter tmp_iter;
1335 
1336  ENTER("model %p, iter (%p)%s", model, iter, iter_to_string(model, iter));
1337 
1338  /* We're adding a row, so the lists on which this model is based have
1339  * changed. Since existing iterators (except the one just passed in)
1340  * are all based on old indexes into those lists, we need to invalidate
1341  * them, which we can do by changing the model's stamp. */
1342  do
1343  {
1344  model->stamp++;
1345  }
1346  while (model->stamp == 0);
1347  iter->stamp = model->stamp;
1348 
1349  /* Tag the new row as inserted. */
1350  tree_model = GTK_TREE_MODEL(model);
1351  path = gnc_tree_model_price_get_path (tree_model, iter);
1352  gtk_tree_model_row_inserted (tree_model, path, iter);
1353 
1354  /* Inform all ancestors. */
1355  /*
1356  * Charles Day: I don't think calls to gtk_tree_model_row_changed() should
1357  * be necessary. It is just a workaround for bug #540201.
1358  */
1359  if (gtk_tree_path_up(path) &&
1360  gtk_tree_path_get_depth(path) > 0 &&
1361  gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
1362  {
1363  /* Signal the change to the parent. */
1364  gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
1365 
1366  /* Is this the parent's first child? */
1367  if (gtk_tree_model_iter_n_children(tree_model, &tmp_iter) == 1)
1368  gtk_tree_model_row_has_child_toggled(tree_model, path, &tmp_iter);
1369 
1370  /* Signal any other ancestors. */
1371  while (gtk_tree_path_up(path) &&
1372  gtk_tree_path_get_depth(path) > 0 &&
1373  gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
1374  {
1375  gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
1376  }
1377  }
1378  gtk_tree_path_free(path);
1379 
1380  /* If the new row already has children, signal that so the expander
1381  * can be shown. This can happen, for example, if a namespace or
1382  * commodity is changed in another place (like the Security Editor)
1383  * and gets removed and then re-added to the commodity db. */
1384  if (gnc_tree_model_price_iter_has_child(tree_model, iter))
1385  {
1386  path = gnc_tree_model_price_get_path(tree_model, iter);
1387  gtk_tree_model_row_has_child_toggled(tree_model, path, iter);
1388  gtk_tree_path_free(path);
1389  }
1390 
1391  LEAVE(" ");
1392 }
1393 
1405 static void
1406 gnc_tree_model_price_row_delete (GncTreeModelPrice *model,
1407  GtkTreePath *path)
1408 {
1409  GtkTreeModel *tree_model;
1410  GtkTreeIter iter;
1411 
1412  g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
1413  g_return_if_fail(path);
1414 
1415  debug_path(ENTER, path);
1416 
1417  tree_model = GTK_TREE_MODEL(model);
1418 
1419  /* We're removing a row, so the lists on which this model is based have
1420  * changed. Since existing iterators are all based on old indexes into
1421  * those lists, we need to invalidate them, which we can do by changing
1422  * the model's stamp. */
1423  do
1424  {
1425  model->stamp++;
1426  }
1427  while (model->stamp == 0);
1428 
1429  /* Signal that the path has been deleted. */
1430  gtk_tree_model_row_deleted(tree_model, path);
1431 
1432  /* Issue any appropriate signals to ancestors. */
1433  if (gtk_tree_path_up(path) &&
1434  gtk_tree_path_get_depth(path) > 0 &&
1435  gtk_tree_model_get_iter(tree_model, &iter, path))
1436  {
1437  DEBUG("iter %s", iter_to_string(model, &iter));
1438 
1439  /* Signal the change to the parent. */
1440  gtk_tree_model_row_changed(tree_model, path, &iter);
1441 
1442  /* Was this the parent's only child? */
1443  if (!gtk_tree_model_iter_has_child(tree_model, &iter))
1444  gtk_tree_model_row_has_child_toggled(tree_model, path, &iter);
1445 
1446  /* Signal any other ancestors. */
1447  while (gtk_tree_path_up(path) &&
1448  gtk_tree_path_get_depth(path) > 0 &&
1449  gtk_tree_model_get_iter(tree_model, &iter, path))
1450  {
1451  DEBUG("iter %s", iter_to_string(model, &iter));
1452  gtk_tree_model_row_changed(tree_model, path, &iter);
1453  }
1454  }
1455 
1456  LEAVE(" ");
1457 }
1458 
1459 
1476 static gboolean
1477 gnc_tree_model_price_do_deletions (gpointer price_db)
1478 {
1479  ENTER(" ");
1480 
1481  /* Go through the list of paths needing removal. */
1482  while (pending_removals)
1483  {
1484  remove_data *data = pending_removals->data;
1485  pending_removals = g_slist_delete_link(pending_removals, pending_removals);
1486 
1487  if (data)
1488  {
1489  debug_path(DEBUG, data->path);
1490 
1491  /* Remove the path. */
1492  gnc_tree_model_price_row_delete(data->model, data->path);
1493  gnc_pricedb_nth_price_reset_cache (price_db);
1494 
1495  gtk_tree_path_free(data->path);
1496  g_free(data);
1497  }
1498  }
1499 
1500  LEAVE(" ");
1501  /* Don't call me again. */
1502  return FALSE;
1503 }
1504 
1505 
1537 static void
1538 gnc_tree_model_price_event_handler (QofInstance *entity,
1539  QofEventId event_type,
1540  gpointer user_data,
1541  gpointer event_data)
1542 {
1543  GncTreeModelPrice *model;
1545  GtkTreePath *path;
1546  GtkTreeIter iter;
1547  remove_data *data;
1548  const gchar *name;
1549 
1550  ENTER("entity %p, event %d, model %p, event data %p",
1551  entity, event_type, user_data, event_data);
1552  model = (GncTreeModelPrice *)user_data;
1553  priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
1554 
1555  /* Do deletions if any are pending. */
1556  if (pending_removals)
1557  gnc_tree_model_price_do_deletions (priv->price_db);
1558 
1559  /* hard failures */
1560  g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
1561 
1562  /* get type specific data */
1563  if (GNC_IS_COMMODITY(entity))
1564  {
1565  gnc_commodity *commodity;
1566 
1567  commodity = GNC_COMMODITY(entity);
1568  name = gnc_commodity_get_mnemonic(commodity);
1569  if (event_type != QOF_EVENT_DESTROY)
1570  {
1571  if (!gnc_tree_model_price_get_iter_from_commodity (model, commodity, &iter))
1572  {
1573  LEAVE("no iter");
1574  return;
1575  }
1576  }
1577  }
1578  else if (GNC_IS_COMMODITY_NAMESPACE(entity))
1579  {
1580  gnc_commodity_namespace *name_space;
1581 
1582  name_space = GNC_COMMODITY_NAMESPACE(entity);
1583  name = gnc_commodity_namespace_get_name(name_space);
1584  if (event_type != QOF_EVENT_DESTROY)
1585  {
1586  if (!gnc_tree_model_price_get_iter_from_namespace (model, name_space, &iter))
1587  {
1588  LEAVE("no iter");
1589  return;
1590  }
1591  }
1592  }
1593  else if (GNC_IS_PRICE(entity))
1594  {
1595  GNCPrice *price;
1596 
1597  price = GNC_PRICE(entity);
1598  name = "price";
1599  if (event_type != QOF_EVENT_DESTROY)
1600  {
1601  if (!gnc_tree_model_price_get_iter_from_price (model, price, &iter))
1602  {
1603  LEAVE("no iter");
1604  return;
1605  }
1606  }
1607  }
1608  else
1609  {
1610  LEAVE(" ");
1611  return;
1612  }
1613 
1614  switch (event_type)
1615  {
1616  case QOF_EVENT_ADD:
1617  /* Tell the filters/views where the new price was added. */
1618  DEBUG("add %s", name);
1619  gnc_pricedb_nth_price_reset_cache (priv->price_db);
1620  gnc_tree_model_price_row_add (model, &iter);
1621  break;
1622 
1623  case QOF_EVENT_REMOVE:
1624  /* Record the path of this account for later use in destruction */
1625  DEBUG("remove %s", name);
1626  path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
1627  if (path == NULL)
1628  {
1629  LEAVE("not in model");
1630  return;
1631  }
1632 
1633  data = g_new0 (remove_data, 1);
1634  data->model = model;
1635  data->path = path;
1636  pending_removals = g_slist_append (pending_removals, data);
1637  g_idle_add_full(G_PRIORITY_HIGH_IDLE,
1638  gnc_tree_model_price_do_deletions, priv->price_db, NULL);
1639 
1640  LEAVE(" ");
1641  return;
1642 
1643  case QOF_EVENT_MODIFY:
1644  DEBUG("change %s", name);
1645  path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
1646  if (path == NULL)
1647  {
1648  LEAVE("not in model");
1649  return;
1650  }
1651  if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &iter, path))
1652  {
1653  gtk_tree_path_free(path);
1654  LEAVE("can't find iter for path");
1655  return;
1656  }
1657  gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter);
1658  gtk_tree_path_free(path);
1659  LEAVE(" ");
1660  return;
1661 
1662  default:
1663  LEAVE("ignored event for %s", name);
1664  return;
1665  }
1666  LEAVE(" new stamp %u", model->stamp);
1667 }
void gnc_price_list_destroy(PriceList *prices)
gnc_price_list_destroy - destroy the given price list, calling gnc_price_unref on all the prices incl...
Definition: gnc-pricedb.c:791
gboolean gnc_tree_model_price_get_iter_from_price(GncTreeModelPrice *model, GNCPrice *price, GtkTreeIter *iter)
Convert a price pointer into a GtkTreeIter.
The instance private data for a price database tree model.
GNCPrice * gnc_pricedb_nth_price(GNCPriceDB *db, const gnc_commodity *c, const int n)
Get the nth price for the given commodity in reverse date order.
Definition: gnc-pricedb.c:2186
gnc_commodity * gnc_tree_model_price_get_commodity(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash commodity.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
int stamp
The state of the model.
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...
gboolean gnc_tree_model_price_get_iter_from_namespace(GncTreeModelPrice *model, gnc_commodity_namespace *name_space, GtkTreeIter *iter)
Convert a commodity namespace pointer into a GtkTreeIter.
utility functions for the GnuCash UI
GtkTreeModel implementation for gnucash price database.
int gnc_pricedb_num_prices(GNCPriceDB *db, const gnc_commodity *c)
Get the number of prices, in any currency, for a given commodity.
Definition: gnc-pricedb.c:2140
#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.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Definition: gnc-ui-util.c:1859
gnc_commodity_namespace * gnc_tree_model_price_get_namespace(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash commodity namespace.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
gboolean gnc_tree_model_price_iter_is_namespace(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a "commodity namespace".
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
The class data structure for a price tree model.
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
Gobject helper routines.
gboolean gnc_tree_model_price_iter_is_commodity(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a commodity.
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:114
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.
gboolean gnc_pricedb_has_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Report whether the pricedb contains prices for one commodity in another.
Definition: gnc-pricedb.c:2070
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.
GtkTreePath * gnc_tree_model_price_get_path_from_price(GncTreeModelPrice *model, GNCPrice *price)
Convert a price pointer into a GtkTreePath.
gnc_commodity_namespace * gnc_commodity_get_namespace_ds(const gnc_commodity *cm)
Retrieve the namespace data structure for the specified commodity.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
Retrieves arbitrary pointers to structs stored by qof_book_set_data.
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
The instance data structure for a price tree model.
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:581
gboolean gnc_tree_model_price_get_iter_from_commodity(GncTreeModelPrice *model, gnc_commodity *commodity, GtkTreeIter *iter)
Convert a commodity pointer into a GtkTreeIter.
PriceList * gnc_pricedb_get_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Return all the prices for a given commodity in another.
Definition: gnc-pricedb.c:2109