GnuCash  5.6-150-g038405b370+
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_finalize (GObject *object);
86 static void gnc_tree_model_price_dispose (GObject *object);
87 
88 static void gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface);
89 static guint gnc_tree_model_price_get_flags (GtkTreeModel *tree_model);
90 static int gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model);
91 static GType gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
92  int index);
93 static gboolean gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
94  GtkTreeIter *iter,
95  GtkTreePath *path);
96 static GtkTreePath *gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
97  GtkTreeIter *iter);
98 static void gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
99  GtkTreeIter *iter,
100  int column,
101  GValue *value);
102 static gboolean gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
103  GtkTreeIter *iter);
104 static gboolean gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
105  GtkTreeIter *iter,
106  GtkTreeIter *parent);
107 static gboolean gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
108  GtkTreeIter *iter);
109 static int gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
110  GtkTreeIter *iter);
111 static gboolean gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
112  GtkTreeIter *iter,
113  GtkTreeIter *parent,
114  int n);
115 static gboolean gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
116  GtkTreeIter *iter,
117  GtkTreeIter *child);
118 static void gnc_tree_model_price_event_handler (QofInstance *entity,
119  QofEventId event_type,
120  gpointer user_data,
121  gpointer event_data);
122 
125 {
126  GncTreeModel gnc_tree_model;
127  int stamp;
129  QofBook *book;
130  GNCPriceDB *price_db;
131  gint event_handler_id;
132  GNCPrintAmountInfo print_info;
133 };
134 
135 G_DEFINE_TYPE_WITH_CODE(GncTreeModelPrice, gnc_tree_model_price, GNC_TYPE_TREE_MODEL,
136  G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL,
137  gnc_tree_model_price_tree_model_init))
138 
139 static void
140 gnc_tree_model_price_class_init (GncTreeModelPriceClass *klass)
141 {
142  GObjectClass *o_class = G_OBJECT_CLASS (klass);
143 
144  o_class->finalize = gnc_tree_model_price_finalize;
145  o_class->dispose = gnc_tree_model_price_dispose;
146 }
147 
148 static void
149 gnc_tree_model_price_init (GncTreeModelPrice *model)
150 {
151  while (model->stamp == 0)
152  {
153  model->stamp = g_random_int ();
154  }
155 
156  model->print_info = gnc_default_price_print_info(NULL);
157 }
158 
159 static void
160 gnc_tree_model_price_finalize (GObject *object)
161 {
162  GncTreeModelPrice *model;
163 
164  ENTER("model %p", object);
165 
166  g_return_if_fail (object != NULL);
167  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (object));
168 
169  model = GNC_TREE_MODEL_PRICE (object);
170 
171  model->book = NULL;
172  model->price_db = NULL;
173 
174  G_OBJECT_CLASS (gnc_tree_model_price_parent_class)->finalize (object);
175  LEAVE(" ");
176 }
177 
178 static void
179 gnc_tree_model_price_dispose (GObject *object)
180 {
181  GncTreeModelPrice *model;
182 
183  ENTER("model %p", object);
184  g_return_if_fail (object != NULL);
185  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (object));
186 
187  model = GNC_TREE_MODEL_PRICE (object);
188 
189  if (model->event_handler_id)
190  {
191  qof_event_unregister_handler (model->event_handler_id);
192  model->event_handler_id = 0;
193  }
194 
195  G_OBJECT_CLASS (gnc_tree_model_price_parent_class)->dispose (object);
196  LEAVE(" ");
197 }
198 
199 GtkTreeModel *
200 gnc_tree_model_price_new (QofBook *book, GNCPriceDB *price_db)
201 {
202  GncTreeModelPrice *model;
203  const GList *item;
204 
205  ENTER(" ");
206 
207  item = gnc_gobject_tracking_get_list(GNC_TREE_MODEL_PRICE_NAME);
208  for ( ; item; item = g_list_next(item))
209  {
210  model = (GncTreeModelPrice *)item->data;
211  if (model->price_db == price_db)
212  {
213  g_object_ref(G_OBJECT(model));
214  LEAVE("returning existing model %p", model);
215  return GTK_TREE_MODEL(model);
216  }
217  }
218 
219  model = g_object_new (GNC_TYPE_TREE_MODEL_PRICE, NULL);
220 
221  model->book = book;
222  model->price_db = price_db;
223 
224  model->event_handler_id =
225  qof_event_register_handler (gnc_tree_model_price_event_handler, model);
226 
227  LEAVE("returning new model %p", model);
228  return GTK_TREE_MODEL (model);
229 }
230 
231 gboolean
232 gnc_tree_model_price_iter_is_namespace (GncTreeModelPrice *model,
233  GtkTreeIter *iter)
234 {
235  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
236  g_return_val_if_fail (iter != NULL, FALSE);
237  g_return_val_if_fail (iter->user_data != NULL, FALSE);
238  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
239 
240  return (iter->user_data == ITER_IS_NAMESPACE);
241 }
242 
243 gboolean
244 gnc_tree_model_price_iter_is_commodity (GncTreeModelPrice *model,
245  GtkTreeIter *iter)
246 {
247  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
248  g_return_val_if_fail (iter != NULL, FALSE);
249  g_return_val_if_fail (iter->user_data != NULL, FALSE);
250  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
251 
252  return (iter->user_data == ITER_IS_COMMODITY);
253 }
254 
255 gboolean
256 gnc_tree_model_price_iter_is_price (GncTreeModelPrice *model,
257  GtkTreeIter *iter)
258 {
259  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
260  g_return_val_if_fail (iter != NULL, FALSE);
261  g_return_val_if_fail (iter->user_data != NULL, FALSE);
262  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
263 
264  return (iter->user_data == ITER_IS_PRICE);
265 }
266 
267 gnc_commodity_namespace *
268 gnc_tree_model_price_get_namespace (GncTreeModelPrice *model,
269  GtkTreeIter *iter)
270 {
271  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
272  g_return_val_if_fail (iter != NULL, NULL);
273  g_return_val_if_fail (iter->user_data != NULL, NULL);
274  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
275 
276  if (iter->user_data != ITER_IS_NAMESPACE)
277  return NULL;
278  return (gnc_commodity_namespace *)iter->user_data2;
279 }
280 
281 gnc_commodity *
282 gnc_tree_model_price_get_commodity (GncTreeModelPrice *model,
283  GtkTreeIter *iter)
284 {
285  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
286  g_return_val_if_fail (iter != NULL, NULL);
287  g_return_val_if_fail (iter->user_data != NULL, NULL);
288  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
289 
290  if (iter->user_data != ITER_IS_COMMODITY)
291  return NULL;
292  return (gnc_commodity *)iter->user_data2;
293 }
294 
295 GNCPrice *
296 gnc_tree_model_price_get_price (GncTreeModelPrice *model,
297  GtkTreeIter *iter)
298 {
299  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
300  g_return_val_if_fail (iter != NULL, NULL);
301  g_return_val_if_fail (iter->user_data != NULL, NULL);
302  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
303 
304  if (iter->user_data != ITER_IS_PRICE)
305  return NULL;
306  return (GNCPrice *)iter->user_data2;
307 }
308 
309 /************************************************************/
310 /* Gnc Tree Model Debugging Utility Function */
311 /************************************************************/
312 
313 #define debug_path(fn, path) { \
314  gchar *path_string = gtk_tree_path_to_string(path); \
315  fn("tree path %s", path_string? path_string : "(NULL)"); \
316  g_free(path_string); \
317  }
318 
319 #define ITER_STRING_LEN 256
320 
321 static const gchar *
322 iter_to_string (GncTreeModelPrice *model, GtkTreeIter *iter)
323 {
324  gnc_commodity_namespace *name_space;
325  gnc_commodity *commodity;
326  GNCPrice *price;
327 #ifdef G_THREADS_ENABLED
328  static GPrivate gtmits_buffer_key = G_PRIVATE_INIT(g_free);
329  gchar *string;
330 
331  string = g_private_get (&gtmits_buffer_key);
332  if (string == NULL)
333  {
334  string = g_malloc(ITER_STRING_LEN + 1);
335  g_private_set (&gtmits_buffer_key, string);
336  }
337 #else
338  static char string[ITER_STRING_LEN + 1];
339 #endif
340 
341  if (iter)
342  {
343  switch (GPOINTER_TO_INT(iter->user_data))
344  {
345  case GPOINTER_TO_INT(ITER_IS_NAMESPACE):
346  name_space = (gnc_commodity_namespace *) iter->user_data2;
347  snprintf(string, ITER_STRING_LEN,
348  "[stamp:%x data:%d (NAMESPACE), %p (%s), %d]",
349  iter->stamp, GPOINTER_TO_INT(iter->user_data),
350  iter->user_data2, gnc_commodity_namespace_get_name (name_space),
351  GPOINTER_TO_INT(iter->user_data3));
352  break;
353 
354  case GPOINTER_TO_INT(ITER_IS_COMMODITY):
355  commodity = (gnc_commodity *) iter->user_data2;
356  snprintf(string, ITER_STRING_LEN,
357  "[stamp:%x data:%d (COMMODITY), %p (%s), %d]",
358  iter->stamp, GPOINTER_TO_INT(iter->user_data),
359  iter->user_data2, gnc_commodity_get_mnemonic (commodity),
360  GPOINTER_TO_INT(iter->user_data3));
361  break;
362 
363  case GPOINTER_TO_INT(ITER_IS_PRICE):
364  price = (GNCPrice *) iter->user_data2;
365  commodity = gnc_price_get_commodity(price);
366  snprintf(string, ITER_STRING_LEN,
367  "[stamp:%x data:%d (PRICE), %p (%s:%s), %d]",
368  iter->stamp, GPOINTER_TO_INT(iter->user_data),
369  iter->user_data2, gnc_commodity_get_mnemonic (commodity),
370  xaccPrintAmount (gnc_price_get_value (price), model->print_info),
371  GPOINTER_TO_INT(iter->user_data3));
372  break;
373 
374  default:
375  snprintf(string, ITER_STRING_LEN,
376  "[stamp:%x data:%d (UNKNOWN), %p, %d]",
377  iter->stamp,
378  GPOINTER_TO_INT(iter->user_data),
379  iter->user_data2,
380  GPOINTER_TO_INT(iter->user_data3));
381  break;
382  }
383  }
384  return string;
385 }
386 
387 
388 /************************************************************/
389 /* Gtk Tree Model Required Interface Functions */
390 /************************************************************/
391 
392 static void
393 gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface)
394 {
395  iface->get_flags = gnc_tree_model_price_get_flags;
396  iface->get_n_columns = gnc_tree_model_price_get_n_columns;
397  iface->get_column_type = gnc_tree_model_price_get_column_type;
398  iface->get_iter = gnc_tree_model_price_get_iter;
399  iface->get_path = gnc_tree_model_price_get_path;
400  iface->get_value = gnc_tree_model_price_get_value;
401  iface->iter_next = gnc_tree_model_price_iter_next;
402  iface->iter_children = gnc_tree_model_price_iter_children;
403  iface->iter_has_child = gnc_tree_model_price_iter_has_child;
404  iface->iter_n_children = gnc_tree_model_price_iter_n_children;
405  iface->iter_nth_child = gnc_tree_model_price_iter_nth_child;
406  iface->iter_parent = gnc_tree_model_price_iter_parent;
407 }
408 
409 static guint
410 gnc_tree_model_price_get_flags (GtkTreeModel *tree_model)
411 {
412  return 0;
413 }
414 
415 static int
416 gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model)
417 {
418  return GNC_TREE_MODEL_PRICE_NUM_COLUMNS;
419 }
420 
421 static GType
422 gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
423  int index)
424 {
425  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), G_TYPE_INVALID);
426  g_return_val_if_fail ((index < GNC_TREE_MODEL_PRICE_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID);
427 
428  switch (index)
429  {
430  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
431  case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
432  case GNC_TREE_MODEL_PRICE_COL_DATE:
433  case GNC_TREE_MODEL_PRICE_COL_SOURCE:
434  case GNC_TREE_MODEL_PRICE_COL_TYPE:
435  case GNC_TREE_MODEL_PRICE_COL_VALUE:
436  return G_TYPE_STRING;
437  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
438  return G_TYPE_BOOLEAN;
439  default:
440  g_assert_not_reached ();
441  return G_TYPE_INVALID;
442  }
443 }
444 
445 static gboolean
446 gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
447  GtkTreeIter *iter,
448  GtkTreePath *path)
449 {
450  GncTreeModelPrice *model;
451  gnc_commodity_table *ct;
452  gnc_commodity_namespace *name_space;
453  gnc_commodity *commodity = NULL;
454  GNCPrice *price;
455  GList *ns_list, *cm_list;
456  guint i, depth;
457 
458  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
459 
460  depth = gtk_tree_path_get_depth (path);
461  ENTER("model %p, iter %p, path %p (depth %d)", tree_model, iter, path, depth);
462  debug_path(DEBUG, path);
463 
464  /* Check the path depth. */
465  if (depth == 0)
466  {
467  LEAVE("depth too small");
468  return FALSE;
469  }
470  if (depth > 3)
471  {
472  LEAVE("depth too big");
473  return FALSE;
474  }
475 
476  /* Make sure the model has a price db. */
477  model = GNC_TREE_MODEL_PRICE (tree_model);
478  if (model->price_db == NULL)
479  {
480  LEAVE("no price db");
481  return FALSE;
482  }
483 
484  /* Verify the first part of the path: the namespace. */
485  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
487  i = gtk_tree_path_get_indices (path)[0];
488  name_space = g_list_nth_data (ns_list, i);
489  if (!name_space)
490  {
491  LEAVE("invalid path at namespace");
492  return FALSE;
493  }
494 
495  if (depth == 1)
496  {
497  /* Return an iterator for the namespace. */
498  iter->stamp = model->stamp;
499  iter->user_data = ITER_IS_NAMESPACE;
500  iter->user_data2 = name_space;
501  iter->user_data3 = GINT_TO_POINTER(i);
502  LEAVE("iter (ns) %s", iter_to_string(model, iter));
503  return TRUE;
504  }
505 
506  /* Verify the second part of the path: the commodity. */
507  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
508  i = gtk_tree_path_get_indices (path)[1];
509  commodity = g_list_nth_data (cm_list, i);
510  if (!commodity)
511  {
512  LEAVE("invalid path at commodity");
513  return FALSE;
514  }
515 
516  if (depth == 2)
517  {
518  /* Return an iterator for the commodity. */
519  iter->stamp = model->stamp;
520  iter->user_data = ITER_IS_COMMODITY;
521  iter->user_data2 = commodity;
522  iter->user_data3 = GINT_TO_POINTER(i);
523  LEAVE("iter (cm) %s", iter_to_string(model, iter));
524  return TRUE;
525  }
526 
527  /* Verify the third part of the path: the price. */
528  i = gtk_tree_path_get_indices (path)[2];
529  price = gnc_pricedb_nth_price(model->price_db, commodity, i);
530  /* There's a race condition here that I can't resolve.
531  * Comment this check out for now, and we'll handle the
532  * resulting problem elsewhere. */
533 #ifdef RACE_CONDITION_SOLVED
534  if (!price)
535  {
536  LEAVE("invalid path at price");
537  return FALSE;
538  }
539 #endif
540 
541  /* Return an iterator for the price. */
542  iter->stamp = model->stamp;
543  iter->user_data = ITER_IS_PRICE;
544  iter->user_data2 = price;
545  iter->user_data3 = GINT_TO_POINTER(i);
546  LEAVE("iter (pc) %s", iter_to_string(model, iter));
547  return TRUE;
548 }
549 
550 static GtkTreePath *
551 gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
552  GtkTreeIter *iter)
553 {
554  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
555  gnc_commodity_table *ct;
556  gnc_commodity_namespace *name_space;
557  gnc_commodity *commodity;
558  GList *ns_list, *cm_list;
559  GtkTreePath *path;
560 
561  ENTER("model %p, iter %p (%s)", tree_model, iter, iter_to_string(model, iter));
562  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
563  g_return_val_if_fail (iter != NULL, NULL);
564  g_return_val_if_fail (iter->user_data != NULL, NULL);
565  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
566 
567  /* Make sure this model has a price db. */
568  if (model->price_db == NULL)
569  {
570  LEAVE("no price db");
571  return FALSE;
572  }
573 
574  if (iter->user_data == ITER_IS_NAMESPACE)
575  {
576  /* Create a path to the namespace. This is just the index into
577  * the namespace list, which we already stored in user_data3. */
578  path = gtk_tree_path_new ();
579  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
580  debug_path(LEAVE, path);
581  return path;
582  }
583 
584  /* Get the namespaces list. */
585  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
587 
588  if (iter->user_data == ITER_IS_COMMODITY)
589  {
590  /* Create a path to the commodity. */
591  commodity = (gnc_commodity*)iter->user_data2;
592  name_space = gnc_commodity_get_namespace_ds(commodity);
593  path = gtk_tree_path_new ();
594  gtk_tree_path_append_index (path, g_list_index (ns_list, name_space));
595  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
596  debug_path(LEAVE, path);
597  return path;
598  }
599 
600  /* Create a path to the price. */
601  commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
602  name_space = gnc_commodity_get_namespace_ds(commodity);
603  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
604  path = gtk_tree_path_new ();
605  gtk_tree_path_append_index (path, g_list_index (ns_list, name_space));
606  gtk_tree_path_append_index (path, g_list_index (cm_list, commodity));
607  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
608  debug_path(LEAVE, path);
609  return path;
610 }
611 
612 static void
613 gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
614  GtkTreeIter *iter,
615  int column,
616  GValue *value)
617 {
618  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
619  gnc_commodity_namespace *name_space;
620  gnc_commodity *commodity;
621  GNCPrice *price;
622  char datebuff[MAX_DATE_LENGTH + 1];
623  memset (datebuff, 0, sizeof(datebuff));
624 
625  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (model));
626  g_return_if_fail (iter != NULL);
627 #ifdef RACE_CONDITION_SOLVED
628  g_return_if_fail (iter->user_data != NULL);
629 #endif
630  g_return_if_fail (iter->stamp == model->stamp);
631 
632  if (iter->user_data == ITER_IS_NAMESPACE)
633  {
634  name_space = (gnc_commodity_namespace *)iter->user_data2;
635  switch (column)
636  {
637  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
638  g_value_init (value, G_TYPE_STRING);
639  g_value_set_string (value, gnc_commodity_namespace_get_gui_name (name_space));
640  break;
641  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
642  g_value_init (value, G_TYPE_BOOLEAN);
643  g_value_set_boolean (value, FALSE);
644  break;
645  default:
646  g_value_init (value, G_TYPE_STRING);
647  g_value_set_string (value, "");
648  break;
649  }
650  return;
651  }
652 
653  if (iter->user_data == ITER_IS_COMMODITY)
654  {
655  commodity = (gnc_commodity *)iter->user_data2;
656  switch (column)
657  {
658  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
659  g_value_init (value, G_TYPE_STRING);
660  g_value_set_string (value, gnc_commodity_get_printname (commodity));
661  break;
662  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
663  g_value_init (value, G_TYPE_BOOLEAN);
664  g_value_set_boolean (value, FALSE);
665  break;
666  default:
667  g_value_init (value, G_TYPE_STRING);
668  g_value_set_string (value, "");
669  break;
670  }
671  return;
672  }
673 
674  price = (GNCPrice *)iter->user_data2;
675 #ifdef RACE_CONDITION_SOLVED
676  g_return_if_fail (price != NULL);
677 #else
678  if (price == NULL)
679  {
680  switch (column)
681  {
682  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
683  g_value_init (value, G_TYPE_STRING);
684  g_value_set_string (value, "");
685  break;
686  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
687  g_value_init (value, G_TYPE_BOOLEAN);
688  g_value_set_boolean (value, FALSE);
689  break;
690  default:
691  g_value_init (value, G_TYPE_STRING);
692  g_value_set_string (value, "");
693  break;
694  }
695  return;
696  }
697 #endif
698 
699  switch (column)
700  {
701  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
702  g_value_init (value, G_TYPE_STRING);
703  commodity = gnc_price_get_commodity (price);
704  g_value_set_string (value, gnc_commodity_get_printname (commodity));
705  break;
706  case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
707  g_value_init (value, G_TYPE_STRING);
708  commodity = gnc_price_get_currency (price);
709  g_value_set_string (value, gnc_commodity_get_printname (commodity));
710  break;
711  case GNC_TREE_MODEL_PRICE_COL_DATE:
713  gnc_price_get_time64 (price));
714  g_value_init (value, G_TYPE_STRING);
715  g_value_set_string (value, datebuff);
716  break;
717  case GNC_TREE_MODEL_PRICE_COL_SOURCE:
718  g_value_init (value, G_TYPE_STRING);
719  g_value_set_string (value, gettext (gnc_price_get_source_string (price)));
720  break;
721  case GNC_TREE_MODEL_PRICE_COL_TYPE:
722  g_value_init (value, G_TYPE_STRING);
723  g_value_set_string (value, gnc_price_get_typestr (price));
724  break;
725  case GNC_TREE_MODEL_PRICE_COL_VALUE:
726  g_value_init (value, G_TYPE_STRING);
727  g_value_set_string (value, xaccPrintAmount (gnc_price_get_value (price),
728  model->print_info));
729  break;
730  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
731  g_value_init (value, G_TYPE_BOOLEAN);
732  g_value_set_boolean (value, TRUE);
733  break;
734  default:
735  g_assert_not_reached ();
736  }
737 }
738 
739 static gboolean
740 gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
741  GtkTreeIter *iter)
742 {
743  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
744  gnc_commodity_table *ct;
745  gnc_commodity *commodity;
746  gnc_commodity_namespace *name_space;
747  GList *list;
748  gint n;
749 
750  ENTER("model %p, iter %p(%s)", tree_model, iter, iter_to_string(model, iter));
751  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
752  g_return_val_if_fail (iter != NULL, FALSE);
753  g_return_val_if_fail (iter->user_data != NULL, FALSE);
754  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
755 
756  if (iter->user_data == ITER_IS_NAMESPACE)
757  {
758  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
760  n = GPOINTER_TO_INT(iter->user_data3) + 1;
761  iter->user_data2 = g_list_nth_data(list, n);
762  if (iter->user_data2 == NULL)
763  {
764  LEAVE("no next iter");
765  return FALSE;
766  }
767  iter->user_data3 = GINT_TO_POINTER(n);
768  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
769  return TRUE;
770  }
771  else if (iter->user_data == ITER_IS_COMMODITY)
772  {
773  name_space = gnc_commodity_get_namespace_ds((gnc_commodity *)iter->user_data2);
775  n = GPOINTER_TO_INT(iter->user_data3) + 1;
776  iter->user_data2 = g_list_nth_data(list, n);
777  if (iter->user_data2 == NULL)
778  {
779  LEAVE("no next iter");
780  return FALSE;
781  }
782  iter->user_data3 = GINT_TO_POINTER(n);
783  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
784  return TRUE;
785  }
786  else if (iter->user_data == ITER_IS_PRICE)
787  {
788  commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
789  n = GPOINTER_TO_INT(iter->user_data3) + 1;
790  iter->user_data2 = gnc_pricedb_nth_price(model->price_db, commodity, 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
801  {
802  LEAVE("unknown iter type");
803  return FALSE;
804  }
805 }
806 
807 static gboolean
808 gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
809  GtkTreeIter *iter,
810  GtkTreeIter *parent)
811 {
812  GncTreeModelPrice *model;
813  gnc_commodity_table *ct;
814  gnc_commodity_namespace *name_space;
815  gnc_commodity *commodity;
816  GList *list;
817 
818  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
819 
820  model = GNC_TREE_MODEL_PRICE (tree_model);
821  ENTER("model %p, iter %p, parent %p (%s)",
822  tree_model, iter, parent, iter_to_string(model, parent));
823 
824  if (parent == NULL)
825  {
826  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
828  if (list == NULL)
829  {
830  LEAVE("no namespaces");
831  return FALSE;
832  }
833 
834  iter->stamp = model->stamp;
835  iter->user_data = ITER_IS_NAMESPACE;
836  iter->user_data2 = g_list_nth_data(list, 0);
837  iter->user_data3 = GINT_TO_POINTER(0);
838  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
839  return TRUE;
840  }
841 
842  if (parent->user_data == ITER_IS_NAMESPACE)
843  {
844  name_space = (gnc_commodity_namespace *)parent->user_data2;
846  if (list == NULL)
847  {
848  LEAVE("no commodities");
849  return FALSE;
850  }
851 
852  iter->stamp = model->stamp;
853  iter->user_data = ITER_IS_COMMODITY;
854  iter->user_data2 = g_list_nth_data(list, 0);
855  iter->user_data3 = GINT_TO_POINTER(0);
856  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
857  return TRUE;
858  }
859 
860  if (parent->user_data == ITER_IS_COMMODITY)
861  {
862  GNCPrice *price;
863  commodity = (gnc_commodity *)parent->user_data2;
864  price = gnc_pricedb_nth_price(model->price_db, commodity, 0);
865  if (price == NULL)
866  {
867  LEAVE("no prices");
868  return FALSE;
869  }
870  iter->stamp = model->stamp;
871  iter->user_data = ITER_IS_PRICE;
872  iter->user_data2 = price;
873  iter->user_data3 = GINT_TO_POINTER(0);
874  LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
875  return TRUE;
876  }
877 
878  LEAVE("FALSE");
879  return FALSE;
880 }
881 
882 static gboolean
883 gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
884  GtkTreeIter *iter)
885 {
886  GncTreeModelPrice *model;
887  gnc_commodity_namespace *name_space;
888  gnc_commodity *commodity;
889  gboolean result;
890  GList *list;
891 
892  model = GNC_TREE_MODEL_PRICE (tree_model);
893  ENTER("model %p, iter %p (%s)", tree_model,
894  iter, iter_to_string(model, iter));
895  g_return_val_if_fail (tree_model != NULL, FALSE);
896  g_return_val_if_fail (iter != NULL, FALSE);
897 
898  if (iter->user_data == ITER_IS_PRICE)
899  {
900  LEAVE("price has no children");
901  return FALSE;
902  }
903 
904  if (iter->user_data == ITER_IS_NAMESPACE)
905  {
906  name_space = (gnc_commodity_namespace *)iter->user_data2;
908  LEAVE("%s children", list ? "has" : "no");
909  return list != NULL;
910  }
911 
912  if (iter->user_data == ITER_IS_COMMODITY)
913  {
914  commodity = (gnc_commodity *)iter->user_data2;
915  result = gnc_pricedb_has_prices(model->price_db, commodity, NULL);
916  LEAVE("%s children", result ? "has" : "no");
917  return result;
918  }
919 
920  LEAVE("no children (unknown type)");
921  return FALSE;
922 }
923 
924 static int
925 gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
926  GtkTreeIter *iter)
927 {
928  GncTreeModelPrice *model;
929  gnc_commodity_table *ct;
930  gnc_commodity_namespace *name_space;
931  gnc_commodity *commodity;
932  GList *list;
933  gint n;
934 
935  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), -1);
936 
937  model = GNC_TREE_MODEL_PRICE (tree_model);
938  ENTER("model %p, iter %p (%s)", tree_model, iter,
939  iter_to_string(model, iter));
940 
941  if (iter == NULL)
942  {
943  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
945  LEAVE("ns list length %d", g_list_length(list));
946  return g_list_length (list);
947  }
948 
949  if (iter->user_data == ITER_IS_NAMESPACE)
950  {
951  name_space = (gnc_commodity_namespace *)iter->user_data2;
953  LEAVE("cm list length %d", g_list_length(list));
954  return g_list_length (list);
955  }
956 
957  if (iter->user_data == ITER_IS_COMMODITY)
958  {
959  commodity = (gnc_commodity *)iter->user_data2;
960  n = gnc_pricedb_num_prices(model->price_db, commodity);
961  LEAVE("price list length %d", n);
962  return n;
963  }
964 
965  LEAVE("0");
966  return 0;
967 }
968 
969 static gboolean
970 gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
971  GtkTreeIter *iter,
972  GtkTreeIter *parent,
973  int n)
974 {
975  GncTreeModelPrice *model;
976  gnc_commodity_table *ct;
977  gnc_commodity_namespace *name_space;
978  gnc_commodity *commodity;
979  GList *list;
980 
981  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
982  g_return_val_if_fail (iter != NULL, FALSE);
983 
984  model = GNC_TREE_MODEL_PRICE (tree_model);
985  ENTER("model %p, iter %p, parent %p (%s), n %d",
986  tree_model, iter, parent, iter_to_string(model, parent), n);
987 
988  if (parent == NULL)
989  {
990  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
992 
993  iter->stamp = model->stamp;
994  iter->user_data = ITER_IS_NAMESPACE;
995  iter->user_data2 = g_list_nth_data(list, n);
996  iter->user_data3 = GINT_TO_POINTER(n);
997  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
998  return iter->user_data2 != NULL;
999  }
1000 
1001  if (parent->user_data == ITER_IS_NAMESPACE)
1002  {
1003  name_space = (gnc_commodity_namespace *)parent->user_data2;
1004  list = gnc_commodity_namespace_get_commodity_list(name_space);
1005 
1006  iter->stamp = model->stamp;
1007  iter->user_data = ITER_IS_COMMODITY;
1008  iter->user_data2 = g_list_nth_data(list, n);
1009  iter->user_data3 = GINT_TO_POINTER(n);
1010  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
1011  return iter->user_data2 != NULL;
1012  }
1013 
1014  if (parent->user_data == ITER_IS_COMMODITY)
1015  {
1016  commodity = (gnc_commodity *)parent->user_data2;
1017 
1018  iter->stamp = model->stamp;
1019  iter->user_data = ITER_IS_PRICE;
1020  iter->user_data2 = gnc_pricedb_nth_price(model->price_db, commodity, n);
1021  iter->user_data3 = GINT_TO_POINTER(n);
1022  LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
1023  return iter->user_data2 != NULL;
1024  }
1025 
1026  iter->stamp = 0;
1027  LEAVE("FALSE");
1028  return FALSE;
1029 }
1030 
1031 static gboolean
1032 gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
1033  GtkTreeIter *iter,
1034  GtkTreeIter *child)
1035 {
1036  GncTreeModelPrice *model;
1037  gnc_commodity_table *ct;
1038  gnc_commodity * commodity;
1039  gnc_commodity_namespace *name_space;
1040  GList *list;
1041 
1042  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
1043  g_return_val_if_fail (iter != NULL, FALSE);
1044  g_return_val_if_fail (child != NULL, FALSE);
1045 
1046  model = GNC_TREE_MODEL_PRICE (tree_model);
1047  ENTER("model %p, iter %p, child %p (%s)",
1048  tree_model, iter, child, iter_to_string(model, child));
1049 
1050  if (child->user_data == ITER_IS_NAMESPACE)
1051  {
1052  LEAVE("ns has no parent");
1053  return FALSE;
1054  }
1055 
1056  if (child->user_data == ITER_IS_COMMODITY)
1057  {
1058  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
1060  name_space = gnc_commodity_get_namespace_ds((gnc_commodity*)child->user_data2);
1061 
1062  iter->stamp = model->stamp;
1063  iter->user_data = ITER_IS_NAMESPACE;
1064  iter->user_data2 = name_space;
1065  iter->user_data3 = GINT_TO_POINTER(g_list_index(list, name_space));
1066  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
1067  return TRUE;
1068  }
1069 
1070  commodity = gnc_price_get_commodity ((GNCPrice*)child->user_data2);
1071  name_space = gnc_commodity_get_namespace_ds(commodity);
1072  list = gnc_commodity_namespace_get_commodity_list(name_space);
1073 
1074  iter->stamp = model->stamp;
1075  iter->user_data = ITER_IS_COMMODITY;
1076  iter->user_data2 = commodity;
1077  iter->user_data3 = GINT_TO_POINTER(g_list_index(list, commodity));
1078  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
1079  return TRUE;
1080 }
1081 
1082 /************************************************************/
1083 /* Price Tree View Functions */
1084 /************************************************************/
1085 
1086 /*
1087  * Convert a model/price pair into a gtk_tree_model_iter. This
1088  * routine should only be called from the file
1089  * gnc-tree-view-price.c.
1090  */
1091 gboolean
1093  GNCPrice *price,
1094  GtkTreeIter *iter)
1095 {
1096  gnc_commodity *commodity;
1097  GList *list;
1098  gint n;
1099 
1100  ENTER("model %p, price %p, iter %p", model, price, iter);
1101  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1102  g_return_val_if_fail ((price != NULL), FALSE);
1103  g_return_val_if_fail ((iter != NULL), FALSE);
1104 
1105  commodity = gnc_price_get_commodity(price);
1106  if (commodity == NULL)
1107  {
1108  LEAVE("no commodity");
1109  return FALSE;
1110  }
1111 
1112  list = gnc_pricedb_get_prices(model->price_db, commodity, NULL);
1113  if (list == NULL)
1114  {
1115  LEAVE("empty list");
1116  return FALSE;
1117  }
1118 
1119  n = g_list_index(list, price);
1120  if (n == -1)
1121  {
1122  gnc_price_list_destroy(list);
1123  LEAVE("not in list");
1124  return FALSE;
1125  }
1126 
1127  iter->stamp = model->stamp;
1128  iter->user_data = ITER_IS_PRICE;
1129  iter->user_data2 = price;
1130  iter->user_data3 = GINT_TO_POINTER(n);
1131  gnc_price_list_destroy(list);
1132  LEAVE("iter %s", iter_to_string(model, iter));
1133  return TRUE;
1134 }
1135 
1136 /*
1137  * Convert a model/price pair into a gtk_tree_model_path. This
1138  * routine should only be called from the file
1139  * gnc-tree-view-price.c.
1140  */
1141 GtkTreePath *
1143  GNCPrice *price)
1144 {
1145  GtkTreeIter tree_iter;
1146  GtkTreePath *tree_path;
1147 
1148  ENTER("model %p, price %p", model, price);
1149  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
1150  g_return_val_if_fail (price != NULL, NULL);
1151 
1152  if (!gnc_tree_model_price_get_iter_from_price (model, price, &tree_iter))
1153  {
1154  LEAVE("no iter");
1155  return NULL;
1156  }
1157 
1158  tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
1159  if (tree_path)
1160  {
1161  gchar *path_string = gtk_tree_path_to_string(tree_path);
1162  LEAVE("path (2) %s", path_string);
1163  g_free(path_string);
1164  }
1165  else
1166  {
1167  LEAVE("no path");
1168  }
1169  return tree_path;
1170 }
1171 
1172 /*
1173  * Convert a model/commodity pair into a gtk_tree_model_iter. This
1174  * routine should only be called from the file
1175  * gnc-tree-view-price.c.
1176  */
1177 gboolean
1179  gnc_commodity *commodity,
1180  GtkTreeIter *iter)
1181 {
1182  gnc_commodity_namespace *name_space;
1183  GList *list;
1184  gint n;
1185 
1186  ENTER("model %p, commodity %p, iter %p", model, commodity, iter);
1187  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1188  g_return_val_if_fail ((commodity != NULL), FALSE);
1189  g_return_val_if_fail ((iter != NULL), FALSE);
1190 
1191  name_space = gnc_commodity_get_namespace_ds(commodity);
1192  if (name_space == NULL)
1193  {
1194  LEAVE("no namespace");
1195  return FALSE;
1196  }
1197 
1198  list = gnc_commodity_namespace_get_commodity_list(name_space);
1199  if (list == NULL)
1200  {
1201  LEAVE("empty list");
1202  return FALSE;
1203  }
1204 
1205  n = g_list_index(list, commodity);
1206  if (n == -1)
1207  {
1208  LEAVE("commodity not in list");
1209  return FALSE;
1210  }
1211 
1212  iter->stamp = model->stamp;
1213  iter->user_data = ITER_IS_COMMODITY;
1214  iter->user_data2 = commodity;
1215  iter->user_data3 = GINT_TO_POINTER(n);
1216  LEAVE("iter %s", iter_to_string(model, iter));
1217  return TRUE;
1218 }
1219 
1220 /*
1221  * Convert a model/namespace pair into a gtk_tree_model_iter. This
1222  * routine should only be called from the file
1223  * gnc-tree-view-price.c.
1224  */
1225 gboolean
1227  gnc_commodity_namespace *name_space,
1228  GtkTreeIter *iter)
1229 {
1230  gnc_commodity_table *ct;
1231  GList *list;
1232  gint n;
1233 
1234  ENTER("model %p, namespace %p, iter %p", model, name_space, iter);
1235  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1236  g_return_val_if_fail ((name_space != NULL), FALSE);
1237  g_return_val_if_fail ((iter != NULL), FALSE);
1238 
1239  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
1241  if (list == NULL)
1242  {
1243  LEAVE("namespace list empty");
1244  return FALSE;
1245  }
1246 
1247  n = g_list_index(list, name_space);
1248  if (n == -1)
1249  {
1250  LEAVE("namespace not found");
1251  return FALSE;
1252  }
1253 
1254  iter->stamp = model->stamp;
1255  iter->user_data = ITER_IS_NAMESPACE;
1256  iter->user_data2 = name_space;
1257  iter->user_data3 = GINT_TO_POINTER(n);
1258  LEAVE("iter %s", iter_to_string(model, iter));
1259  return TRUE;
1260 }
1261 
1262 /************************************************************/
1263 /* Price Tree Model - Engine Event Handling Functions */
1264 /************************************************************/
1265 
1266 typedef struct _remove_data
1267 {
1268  GncTreeModelPrice *model;
1269  GtkTreePath *path;
1270 } remove_data;
1271 
1272 static GSList *pending_removals = NULL;
1273 
1285 static void
1286 gnc_tree_model_price_row_add (GncTreeModelPrice *model,
1287  GtkTreeIter *iter)
1288 {
1289  GtkTreePath *path;
1290  GtkTreeModel *tree_model;
1291  GtkTreeIter tmp_iter;
1292 
1293  ENTER("model %p, iter (%p)%s", model, iter, iter_to_string(model, iter));
1294 
1295  /* We're adding a row, so the lists on which this model is based have
1296  * changed. Since existing iterators (except the one just passed in)
1297  * are all based on old indexes into those lists, we need to invalidate
1298  * them, which we can do by changing the model's stamp. */
1299  do
1300  {
1301  model->stamp++;
1302  }
1303  while (model->stamp == 0);
1304  iter->stamp = model->stamp;
1305 
1306  /* Tag the new row as inserted. */
1307  tree_model = GTK_TREE_MODEL(model);
1308  path = gnc_tree_model_price_get_path (tree_model, iter);
1309  gtk_tree_model_row_inserted (tree_model, path, iter);
1310 
1311  /* Inform all ancestors. */
1312  /*
1313  * Charles Day: I don't think calls to gtk_tree_model_row_changed() should
1314  * be necessary. It is just a workaround for bug #540201.
1315  */
1316  if (gtk_tree_path_up(path) &&
1317  gtk_tree_path_get_depth(path) > 0 &&
1318  gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
1319  {
1320  /* Signal the change to the parent. */
1321  gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
1322 
1323  /* Is this the parent's first child? */
1324  if (gtk_tree_model_iter_n_children(tree_model, &tmp_iter) == 1)
1325  gtk_tree_model_row_has_child_toggled(tree_model, path, &tmp_iter);
1326 
1327  /* Signal any other ancestors. */
1328  while (gtk_tree_path_up(path) &&
1329  gtk_tree_path_get_depth(path) > 0 &&
1330  gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
1331  {
1332  gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
1333  }
1334  }
1335  gtk_tree_path_free(path);
1336 
1337  /* If the new row already has children, signal that so the expander
1338  * can be shown. This can happen, for example, if a namespace or
1339  * commodity is changed in another place (like the Security Editor)
1340  * and gets removed and then re-added to the commodity db. */
1341  if (gnc_tree_model_price_iter_has_child(tree_model, iter))
1342  {
1343  path = gnc_tree_model_price_get_path(tree_model, iter);
1344  gtk_tree_model_row_has_child_toggled(tree_model, path, iter);
1345  gtk_tree_path_free(path);
1346  }
1347 
1348  LEAVE(" ");
1349 }
1350 
1362 static void
1363 gnc_tree_model_price_row_delete (GncTreeModelPrice *model,
1364  GtkTreePath *path)
1365 {
1366  GtkTreeModel *tree_model;
1367  GtkTreeIter iter;
1368 
1369  g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
1370  g_return_if_fail(path);
1371 
1372  debug_path(ENTER, path);
1373 
1374  tree_model = GTK_TREE_MODEL(model);
1375 
1376  /* We're removing a row, so the lists on which this model is based have
1377  * changed. Since existing iterators are all based on old indexes into
1378  * those lists, we need to invalidate them, which we can do by changing
1379  * the model's stamp. */
1380  do
1381  {
1382  model->stamp++;
1383  }
1384  while (model->stamp == 0);
1385 
1386  /* Signal that the path has been deleted. */
1387  gtk_tree_model_row_deleted(tree_model, path);
1388 
1389  /* Issue any appropriate signals to ancestors. */
1390  if (gtk_tree_path_up(path) &&
1391  gtk_tree_path_get_depth(path) > 0 &&
1392  gtk_tree_model_get_iter(tree_model, &iter, path))
1393  {
1394  DEBUG("iter %s", iter_to_string(model, &iter));
1395 
1396  /* Signal the change to the parent. */
1397  gtk_tree_model_row_changed(tree_model, path, &iter);
1398 
1399  /* Was this the parent's only child? */
1400  if (!gtk_tree_model_iter_has_child(tree_model, &iter))
1401  gtk_tree_model_row_has_child_toggled(tree_model, path, &iter);
1402 
1403  /* Signal any other ancestors. */
1404  while (gtk_tree_path_up(path) &&
1405  gtk_tree_path_get_depth(path) > 0 &&
1406  gtk_tree_model_get_iter(tree_model, &iter, path))
1407  {
1408  DEBUG("iter %s", iter_to_string(model, &iter));
1409  gtk_tree_model_row_changed(tree_model, path, &iter);
1410  }
1411  }
1412 
1413  LEAVE(" ");
1414 }
1415 
1416 
1433 static gboolean
1434 gnc_tree_model_price_do_deletions (gpointer price_db)
1435 {
1436  ENTER(" ");
1437 
1438  /* Go through the list of paths needing removal. */
1439  while (pending_removals)
1440  {
1441  remove_data *data = pending_removals->data;
1442  pending_removals = g_slist_delete_link(pending_removals, pending_removals);
1443 
1444  if (data)
1445  {
1446  debug_path(DEBUG, data->path);
1447 
1448  /* Remove the path. */
1449  gnc_tree_model_price_row_delete(data->model, data->path);
1450  gnc_pricedb_nth_price_reset_cache (price_db);
1451 
1452  gtk_tree_path_free(data->path);
1453  g_free(data);
1454  }
1455  }
1456 
1457  LEAVE(" ");
1458  /* Don't call me again. */
1459  return FALSE;
1460 }
1461 
1462 
1494 static void
1495 gnc_tree_model_price_event_handler (QofInstance *entity,
1496  QofEventId event_type,
1497  gpointer user_data,
1498  gpointer event_data)
1499 {
1500  GncTreeModelPrice *model;
1501  GtkTreePath *path;
1502  GtkTreeIter iter;
1503  remove_data *data;
1504  const gchar *name;
1505 
1506  ENTER("entity %p, event %d, model %p, event data %p",
1507  entity, event_type, user_data, event_data);
1508  model = (GncTreeModelPrice *)user_data;
1509 
1510  /* Do deletions if any are pending. */
1511  if (pending_removals)
1512  gnc_tree_model_price_do_deletions (model->price_db);
1513 
1514  /* hard failures */
1515  g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
1516 
1517  /* get type specific data */
1518  if (GNC_IS_COMMODITY(entity))
1519  {
1520  gnc_commodity *commodity;
1521 
1522  commodity = GNC_COMMODITY(entity);
1523  name = gnc_commodity_get_mnemonic(commodity);
1524  if (event_type != QOF_EVENT_DESTROY)
1525  {
1526  if (!gnc_tree_model_price_get_iter_from_commodity (model, commodity, &iter))
1527  {
1528  LEAVE("no iter");
1529  return;
1530  }
1531  }
1532  }
1533  else if (GNC_IS_COMMODITY_NAMESPACE(entity))
1534  {
1535  gnc_commodity_namespace *name_space;
1536 
1537  name_space = GNC_COMMODITY_NAMESPACE(entity);
1538  name = gnc_commodity_namespace_get_name(name_space);
1539  if (event_type != QOF_EVENT_DESTROY)
1540  {
1541  if (!gnc_tree_model_price_get_iter_from_namespace (model, name_space, &iter))
1542  {
1543  LEAVE("no iter");
1544  return;
1545  }
1546  }
1547  }
1548  else if (GNC_IS_PRICE(entity))
1549  {
1550  GNCPrice *price;
1551 
1552  price = GNC_PRICE(entity);
1553  name = "price";
1554  if (event_type != QOF_EVENT_DESTROY)
1555  {
1556  if (!gnc_tree_model_price_get_iter_from_price (model, price, &iter))
1557  {
1558  LEAVE("no iter");
1559  return;
1560  }
1561  }
1562  }
1563  else
1564  {
1565  LEAVE(" ");
1566  return;
1567  }
1568 
1569  switch (event_type)
1570  {
1571  case QOF_EVENT_ADD:
1572  /* Tell the filters/views where the new price was added. */
1573  DEBUG("add %s", name);
1574  gnc_pricedb_nth_price_reset_cache (model->price_db);
1575  gnc_tree_model_price_row_add (model, &iter);
1576  break;
1577 
1578  case QOF_EVENT_REMOVE:
1579  /* Record the path of this account for later use in destruction */
1580  DEBUG("remove %s", name);
1581  path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
1582  if (path == NULL)
1583  {
1584  LEAVE("not in model");
1585  return;
1586  }
1587 
1588  data = g_new0 (remove_data, 1);
1589  data->model = model;
1590  data->path = path;
1591  pending_removals = g_slist_append (pending_removals, data);
1592  g_idle_add_full(G_PRIORITY_HIGH_IDLE,
1593  gnc_tree_model_price_do_deletions, model->price_db, NULL);
1594 
1595  LEAVE(" ");
1596  return;
1597 
1598  case QOF_EVENT_MODIFY:
1599  DEBUG("change %s", name);
1600  path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
1601  if (path == NULL)
1602  {
1603  LEAVE("not in model");
1604  return;
1605  }
1606  if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &iter, path))
1607  {
1608  gtk_tree_path_free(path);
1609  LEAVE("can't find iter for path");
1610  return;
1611  }
1612  gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter);
1613  gtk_tree_path_free(path);
1614  LEAVE(" ");
1615  return;
1616 
1617  default:
1618  LEAVE("ignored event for %s", name);
1619  return;
1620  }
1621  LEAVE(" new stamp %u", model->stamp);
1622 }
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...
gboolean gnc_tree_model_price_get_iter_from_price(GncTreeModelPrice *model, GNCPrice *price, GtkTreeIter *iter)
Convert a price pointer into a GtkTreeIter.
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.
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.
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.
#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.
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.
G_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_APPLICATION_WINDOW, G_IMPLEMENT_INTERFACE(GNC_TYPE_WINDOW, gnc_window_main_window_init)) static guint main_window_signals[LAST_SIGNAL]
A holding place for all the signals generated by the main window code.
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 instance 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.
GncTreeModel gnc_tree_model
The parent object data.
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
int stamp
The state of the model.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
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.
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.
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:572
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.