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  g_list_free (ns_list);
495 
496  if (depth == 1)
497  {
498  /* Return an iterator for the namespace. */
499  iter->stamp = model->stamp;
500  iter->user_data = ITER_IS_NAMESPACE;
501  iter->user_data2 = name_space;
502  iter->user_data3 = GINT_TO_POINTER(i);
503  LEAVE("iter (ns) %s", iter_to_string(model, iter));
504  return TRUE;
505  }
506 
507  /* Verify the second part of the path: the commodity. */
508  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
509  i = gtk_tree_path_get_indices (path)[1];
510  commodity = g_list_nth_data (cm_list, i);
511  g_list_free (cm_list);
512  if (!commodity)
513  {
514  LEAVE("invalid path at commodity");
515  return FALSE;
516  }
517 
518  if (depth == 2)
519  {
520  /* Return an iterator for the commodity. */
521  iter->stamp = model->stamp;
522  iter->user_data = ITER_IS_COMMODITY;
523  iter->user_data2 = commodity;
524  iter->user_data3 = GINT_TO_POINTER(i);
525  LEAVE("iter (cm) %s", iter_to_string(model, iter));
526  return TRUE;
527  }
528 
529  /* Verify the third part of the path: the price. */
530  i = gtk_tree_path_get_indices (path)[2];
531  price = gnc_pricedb_nth_price(model->price_db, commodity, i);
532  /* There's a race condition here that I can't resolve.
533  * Comment this check out for now, and we'll handle the
534  * resulting problem elsewhere. */
535 #ifdef RACE_CONDITION_SOLVED
536  if (!price)
537  {
538  LEAVE("invalid path at price");
539  return FALSE;
540  }
541 #endif
542 
543  /* Return an iterator for the price. */
544  iter->stamp = model->stamp;
545  iter->user_data = ITER_IS_PRICE;
546  iter->user_data2 = price;
547  iter->user_data3 = GINT_TO_POINTER(i);
548  LEAVE("iter (pc) %s", iter_to_string(model, iter));
549  return TRUE;
550 }
551 
552 static GtkTreePath *
553 gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
554  GtkTreeIter *iter)
555 {
556  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
557  gnc_commodity_table *ct;
558  gnc_commodity_namespace *name_space;
559  gnc_commodity *commodity;
560  GList *ns_list, *cm_list;
561  GtkTreePath *path;
562 
563  ENTER("model %p, iter %p (%s)", tree_model, iter, iter_to_string(model, iter));
564  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
565  g_return_val_if_fail (iter != NULL, NULL);
566  g_return_val_if_fail (iter->user_data != NULL, NULL);
567  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
568 
569  /* Make sure this model has a price db. */
570  if (model->price_db == NULL)
571  {
572  LEAVE("no price db");
573  return FALSE;
574  }
575 
576  if (iter->user_data == ITER_IS_NAMESPACE)
577  {
578  /* Create a path to the namespace. This is just the index into
579  * the namespace list, which we already stored in user_data3. */
580  path = gtk_tree_path_new ();
581  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
582  debug_path(LEAVE, path);
583  return path;
584  }
585 
586  /* Get the namespaces list. */
587  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
589 
590  if (iter->user_data == ITER_IS_COMMODITY)
591  {
592  /* Create a path to the commodity. */
593  commodity = (gnc_commodity*)iter->user_data2;
594  name_space = gnc_commodity_get_namespace_ds(commodity);
595  path = gtk_tree_path_new ();
596  gtk_tree_path_append_index (path, g_list_index (ns_list, name_space));
597  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
598  debug_path(LEAVE, path);
599  g_list_free (ns_list);
600  return path;
601  }
602 
603  /* Create a path to the price. */
604  commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
605  name_space = gnc_commodity_get_namespace_ds(commodity);
606  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
607  path = gtk_tree_path_new ();
608  gtk_tree_path_append_index (path, g_list_index (ns_list, name_space));
609  gtk_tree_path_append_index (path, g_list_index (cm_list, commodity));
610  gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
611  debug_path(LEAVE, path);
612  g_list_free (cm_list);
613  g_list_free (ns_list);
614  return path;
615 }
616 
617 static void
618 gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
619  GtkTreeIter *iter,
620  int column,
621  GValue *value)
622 {
623  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
624  gnc_commodity_namespace *name_space;
625  gnc_commodity *commodity;
626  GNCPrice *price;
627  char datebuff[MAX_DATE_LENGTH + 1];
628  memset (datebuff, 0, sizeof(datebuff));
629 
630  g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (model));
631  g_return_if_fail (iter != NULL);
632 #ifdef RACE_CONDITION_SOLVED
633  g_return_if_fail (iter->user_data != NULL);
634 #endif
635  g_return_if_fail (iter->stamp == model->stamp);
636 
637  if (iter->user_data == ITER_IS_NAMESPACE)
638  {
639  name_space = (gnc_commodity_namespace *)iter->user_data2;
640  switch (column)
641  {
642  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
643  g_value_init (value, G_TYPE_STRING);
644  g_value_set_string (value, gnc_commodity_namespace_get_gui_name (name_space));
645  break;
646  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
647  g_value_init (value, G_TYPE_BOOLEAN);
648  g_value_set_boolean (value, FALSE);
649  break;
650  default:
651  g_value_init (value, G_TYPE_STRING);
652  g_value_set_string (value, "");
653  break;
654  }
655  return;
656  }
657 
658  if (iter->user_data == ITER_IS_COMMODITY)
659  {
660  commodity = (gnc_commodity *)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_get_printname (commodity));
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  price = (GNCPrice *)iter->user_data2;
680 #ifdef RACE_CONDITION_SOLVED
681  g_return_if_fail (price != NULL);
682 #else
683  if (price == NULL)
684  {
685  switch (column)
686  {
687  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
688  g_value_init (value, G_TYPE_STRING);
689  g_value_set_string (value, "");
690  break;
691  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
692  g_value_init (value, G_TYPE_BOOLEAN);
693  g_value_set_boolean (value, FALSE);
694  break;
695  default:
696  g_value_init (value, G_TYPE_STRING);
697  g_value_set_string (value, "");
698  break;
699  }
700  return;
701  }
702 #endif
703 
704  switch (column)
705  {
706  case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
707  g_value_init (value, G_TYPE_STRING);
708  commodity = gnc_price_get_commodity (price);
709  g_value_set_string (value, gnc_commodity_get_printname (commodity));
710  break;
711  case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
712  g_value_init (value, G_TYPE_STRING);
713  commodity = gnc_price_get_currency (price);
714  g_value_set_string (value, gnc_commodity_get_printname (commodity));
715  break;
716  case GNC_TREE_MODEL_PRICE_COL_DATE:
718  gnc_price_get_time64 (price));
719  g_value_init (value, G_TYPE_STRING);
720  g_value_set_string (value, datebuff);
721  break;
722  case GNC_TREE_MODEL_PRICE_COL_SOURCE:
723  g_value_init (value, G_TYPE_STRING);
724  g_value_set_string (value, gettext (gnc_price_get_source_string (price)));
725  break;
726  case GNC_TREE_MODEL_PRICE_COL_TYPE:
727  g_value_init (value, G_TYPE_STRING);
728  g_value_set_string (value, gnc_price_get_typestr (price));
729  break;
730  case GNC_TREE_MODEL_PRICE_COL_VALUE:
731  g_value_init (value, G_TYPE_STRING);
732  g_value_set_string (value, xaccPrintAmount (gnc_price_get_value (price),
733  model->print_info));
734  break;
735  case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
736  g_value_init (value, G_TYPE_BOOLEAN);
737  g_value_set_boolean (value, TRUE);
738  break;
739  default:
740  g_assert_not_reached ();
741  }
742 }
743 
744 static gboolean
745 gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
746  GtkTreeIter *iter)
747 {
748  GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
749  gnc_commodity_table *ct;
750  gnc_commodity *commodity;
751  gnc_commodity_namespace *name_space;
752  GList *list;
753  gint n;
754 
755  ENTER("model %p, iter %p(%s)", tree_model, iter, iter_to_string(model, iter));
756  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
757  g_return_val_if_fail (iter != NULL, FALSE);
758  g_return_val_if_fail (iter->user_data != NULL, FALSE);
759  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
760 
761  if (iter->user_data == ITER_IS_NAMESPACE)
762  {
763  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
765  n = GPOINTER_TO_INT(iter->user_data3) + 1;
766  iter->user_data2 = g_list_nth_data(list, n);
767  g_list_free (list);
768  if (iter->user_data2 == NULL)
769  {
770  LEAVE("no next iter");
771  return FALSE;
772  }
773  iter->user_data3 = GINT_TO_POINTER(n);
774  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
775  return TRUE;
776  }
777  else if (iter->user_data == ITER_IS_COMMODITY)
778  {
779  name_space = gnc_commodity_get_namespace_ds((gnc_commodity *)iter->user_data2);
781  n = GPOINTER_TO_INT(iter->user_data3) + 1;
782  iter->user_data2 = g_list_nth_data(list, n);
783  g_list_free (list);
784  if (iter->user_data2 == NULL)
785  {
786  LEAVE("no next iter");
787  return FALSE;
788  }
789  iter->user_data3 = GINT_TO_POINTER(n);
790  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
791  return TRUE;
792  }
793  else if (iter->user_data == ITER_IS_PRICE)
794  {
795  commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
796  n = GPOINTER_TO_INT(iter->user_data3) + 1;
797  iter->user_data2 = gnc_pricedb_nth_price(model->price_db, commodity, n);
798  if (iter->user_data2 == NULL)
799  {
800  LEAVE("no next iter");
801  return FALSE;
802  }
803  iter->user_data3 = GINT_TO_POINTER(n);
804  LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
805  return TRUE;
806  }
807  else
808  {
809  LEAVE("unknown iter type");
810  return FALSE;
811  }
812 }
813 
814 static gboolean
815 gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
816  GtkTreeIter *iter,
817  GtkTreeIter *parent)
818 {
819  GncTreeModelPrice *model;
820  gnc_commodity_table *ct;
821  gnc_commodity_namespace *name_space;
822  gnc_commodity *commodity;
823  GList *list;
824 
825  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
826 
827  model = GNC_TREE_MODEL_PRICE (tree_model);
828  ENTER("model %p, iter %p, parent %p (%s)",
829  tree_model, iter, parent, iter_to_string(model, parent));
830 
831  if (parent == NULL)
832  {
833  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
835  if (list == NULL)
836  {
837  LEAVE("no namespaces");
838  return FALSE;
839  }
840 
841  iter->stamp = model->stamp;
842  iter->user_data = ITER_IS_NAMESPACE;
843  iter->user_data2 = g_list_nth_data(list, 0);
844  iter->user_data3 = GINT_TO_POINTER(0);
845  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
846  g_list_free (list);
847  return TRUE;
848  }
849 
850  if (parent->user_data == ITER_IS_NAMESPACE)
851  {
852  name_space = (gnc_commodity_namespace *)parent->user_data2;
854  if (list == NULL)
855  {
856  LEAVE("no commodities");
857  return FALSE;
858  }
859 
860  iter->stamp = model->stamp;
861  iter->user_data = ITER_IS_COMMODITY;
862  iter->user_data2 = g_list_nth_data(list, 0);
863  iter->user_data3 = GINT_TO_POINTER(0);
864  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
865  g_list_free (list);
866  return TRUE;
867  }
868 
869  if (parent->user_data == ITER_IS_COMMODITY)
870  {
871  GNCPrice *price;
872  commodity = (gnc_commodity *)parent->user_data2;
873  price = gnc_pricedb_nth_price(model->price_db, commodity, 0);
874  if (price == NULL)
875  {
876  LEAVE("no prices");
877  return FALSE;
878  }
879  iter->stamp = model->stamp;
880  iter->user_data = ITER_IS_PRICE;
881  iter->user_data2 = price;
882  iter->user_data3 = GINT_TO_POINTER(0);
883  LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
884  return TRUE;
885  }
886 
887  LEAVE("FALSE");
888  return FALSE;
889 }
890 
891 static gboolean
892 gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
893  GtkTreeIter *iter)
894 {
895  GncTreeModelPrice *model;
896  gnc_commodity_namespace *name_space;
897  gnc_commodity *commodity;
898  gboolean result;
899  GList *list;
900 
901  model = GNC_TREE_MODEL_PRICE (tree_model);
902  ENTER("model %p, iter %p (%s)", tree_model,
903  iter, iter_to_string(model, iter));
904  g_return_val_if_fail (tree_model != NULL, FALSE);
905  g_return_val_if_fail (iter != NULL, FALSE);
906 
907  if (iter->user_data == ITER_IS_PRICE)
908  {
909  LEAVE("price has no children");
910  return FALSE;
911  }
912 
913  if (iter->user_data == ITER_IS_NAMESPACE)
914  {
915  name_space = (gnc_commodity_namespace *)iter->user_data2;
917  LEAVE("%s children", list ? "has" : "no");
918  gboolean rv = (list != NULL);
919  g_list_free (list);
920  return rv;
921  }
922 
923  if (iter->user_data == ITER_IS_COMMODITY)
924  {
925  commodity = (gnc_commodity *)iter->user_data2;
926  result = gnc_pricedb_has_prices(model->price_db, commodity, NULL);
927  LEAVE("%s children", result ? "has" : "no");
928  return result;
929  }
930 
931  LEAVE("no children (unknown type)");
932  return FALSE;
933 }
934 
935 static int
936 gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
937  GtkTreeIter *iter)
938 {
939  GncTreeModelPrice *model;
940  gnc_commodity_table *ct;
941  gnc_commodity_namespace *name_space;
942  gnc_commodity *commodity;
943  GList *list;
944  gint n;
945 
946  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), -1);
947 
948  model = GNC_TREE_MODEL_PRICE (tree_model);
949  ENTER("model %p, iter %p (%s)", tree_model, iter,
950  iter_to_string(model, iter));
951 
952  if (iter == NULL)
953  {
954  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
956  LEAVE("ns list length %d", g_list_length(list));
957  guint rv = g_list_length (list);
958  g_list_free (list);
959  return rv;
960  }
961 
962  if (iter->user_data == ITER_IS_NAMESPACE)
963  {
964  name_space = (gnc_commodity_namespace *)iter->user_data2;
966  LEAVE("cm list length %d", g_list_length(list));
967  guint rv = g_list_length (list);
968  g_list_free (list);
969  return rv;
970  }
971 
972  if (iter->user_data == ITER_IS_COMMODITY)
973  {
974  commodity = (gnc_commodity *)iter->user_data2;
975  n = gnc_pricedb_num_prices(model->price_db, commodity);
976  LEAVE("price list length %d", n);
977  return n;
978  }
979 
980  LEAVE("0");
981  return 0;
982 }
983 
984 static gboolean
985 gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
986  GtkTreeIter *iter,
987  GtkTreeIter *parent,
988  int n)
989 {
990  GncTreeModelPrice *model;
991  gnc_commodity_table *ct;
992  gnc_commodity_namespace *name_space;
993  gnc_commodity *commodity;
994  GList *list;
995 
996  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
997  g_return_val_if_fail (iter != NULL, FALSE);
998 
999  model = GNC_TREE_MODEL_PRICE (tree_model);
1000  ENTER("model %p, iter %p, parent %p (%s), n %d",
1001  tree_model, iter, parent, iter_to_string(model, parent), n);
1002 
1003  if (parent == NULL)
1004  {
1005  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
1007 
1008  iter->stamp = model->stamp;
1009  iter->user_data = ITER_IS_NAMESPACE;
1010  iter->user_data2 = g_list_nth_data(list, n);
1011  iter->user_data3 = GINT_TO_POINTER(n);
1012  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
1013  g_list_free (list);
1014  return iter->user_data2 != NULL;
1015  }
1016 
1017  if (parent->user_data == ITER_IS_NAMESPACE)
1018  {
1019  name_space = (gnc_commodity_namespace *)parent->user_data2;
1020  list = gnc_commodity_namespace_get_commodity_list(name_space);
1021 
1022  iter->stamp = model->stamp;
1023  iter->user_data = ITER_IS_COMMODITY;
1024  iter->user_data2 = g_list_nth_data(list, n);
1025  iter->user_data3 = GINT_TO_POINTER(n);
1026  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
1027  g_list_free (list);
1028  return iter->user_data2 != NULL;
1029  }
1030 
1031  if (parent->user_data == ITER_IS_COMMODITY)
1032  {
1033  commodity = (gnc_commodity *)parent->user_data2;
1034 
1035  iter->stamp = model->stamp;
1036  iter->user_data = ITER_IS_PRICE;
1037  iter->user_data2 = gnc_pricedb_nth_price(model->price_db, commodity, n);
1038  iter->user_data3 = GINT_TO_POINTER(n);
1039  LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
1040  return iter->user_data2 != NULL;
1041  }
1042 
1043  iter->stamp = 0;
1044  LEAVE("FALSE");
1045  return FALSE;
1046 }
1047 
1048 static gboolean
1049 gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
1050  GtkTreeIter *iter,
1051  GtkTreeIter *child)
1052 {
1053  GncTreeModelPrice *model;
1054  gnc_commodity_table *ct;
1055  gnc_commodity * commodity;
1056  gnc_commodity_namespace *name_space;
1057  GList *list;
1058 
1059  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
1060  g_return_val_if_fail (iter != NULL, FALSE);
1061  g_return_val_if_fail (child != NULL, FALSE);
1062 
1063  model = GNC_TREE_MODEL_PRICE (tree_model);
1064  ENTER("model %p, iter %p, child %p (%s)",
1065  tree_model, iter, child, iter_to_string(model, child));
1066 
1067  if (child->user_data == ITER_IS_NAMESPACE)
1068  {
1069  LEAVE("ns has no parent");
1070  return FALSE;
1071  }
1072 
1073  if (child->user_data == ITER_IS_COMMODITY)
1074  {
1075  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
1077  name_space = gnc_commodity_get_namespace_ds((gnc_commodity*)child->user_data2);
1078 
1079  iter->stamp = model->stamp;
1080  iter->user_data = ITER_IS_NAMESPACE;
1081  iter->user_data2 = name_space;
1082  iter->user_data3 = GINT_TO_POINTER(g_list_index(list, name_space));
1083  LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
1084  g_list_free (list);
1085  return TRUE;
1086  }
1087 
1088  commodity = gnc_price_get_commodity ((GNCPrice*)child->user_data2);
1089  name_space = gnc_commodity_get_namespace_ds(commodity);
1090  list = gnc_commodity_namespace_get_commodity_list(name_space);
1091 
1092  iter->stamp = model->stamp;
1093  iter->user_data = ITER_IS_COMMODITY;
1094  iter->user_data2 = commodity;
1095  iter->user_data3 = GINT_TO_POINTER(g_list_index(list, commodity));
1096  LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
1097  g_list_free (list);
1098  return TRUE;
1099 }
1100 
1101 /************************************************************/
1102 /* Price Tree View Functions */
1103 /************************************************************/
1104 
1105 /*
1106  * Convert a model/price pair into a gtk_tree_model_iter. This
1107  * routine should only be called from the file
1108  * gnc-tree-view-price.c.
1109  */
1110 gboolean
1112  GNCPrice *price,
1113  GtkTreeIter *iter)
1114 {
1115  gnc_commodity *commodity;
1116  GList *list;
1117  gint n;
1118 
1119  ENTER("model %p, price %p, iter %p", model, price, iter);
1120  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1121  g_return_val_if_fail ((price != NULL), FALSE);
1122  g_return_val_if_fail ((iter != NULL), FALSE);
1123 
1124  commodity = gnc_price_get_commodity(price);
1125  if (commodity == NULL)
1126  {
1127  LEAVE("no commodity");
1128  return FALSE;
1129  }
1130 
1131  list = gnc_pricedb_get_prices(model->price_db, commodity, NULL);
1132  if (list == NULL)
1133  {
1134  LEAVE("empty list");
1135  return FALSE;
1136  }
1137 
1138  n = g_list_index(list, price);
1139  if (n == -1)
1140  {
1141  gnc_price_list_destroy(list);
1142  LEAVE("not in list");
1143  return FALSE;
1144  }
1145 
1146  iter->stamp = model->stamp;
1147  iter->user_data = ITER_IS_PRICE;
1148  iter->user_data2 = price;
1149  iter->user_data3 = GINT_TO_POINTER(n);
1150  gnc_price_list_destroy(list);
1151  LEAVE("iter %s", iter_to_string(model, iter));
1152  return TRUE;
1153 }
1154 
1155 /*
1156  * Convert a model/price pair into a gtk_tree_model_path. This
1157  * routine should only be called from the file
1158  * gnc-tree-view-price.c.
1159  */
1160 GtkTreePath *
1162  GNCPrice *price)
1163 {
1164  GtkTreeIter tree_iter;
1165  GtkTreePath *tree_path;
1166 
1167  ENTER("model %p, price %p", model, price);
1168  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
1169  g_return_val_if_fail (price != NULL, NULL);
1170 
1171  if (!gnc_tree_model_price_get_iter_from_price (model, price, &tree_iter))
1172  {
1173  LEAVE("no iter");
1174  return NULL;
1175  }
1176 
1177  tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
1178  if (tree_path)
1179  {
1180  gchar *path_string = gtk_tree_path_to_string(tree_path);
1181  LEAVE("path (2) %s", path_string);
1182  g_free(path_string);
1183  }
1184  else
1185  {
1186  LEAVE("no path");
1187  }
1188  return tree_path;
1189 }
1190 
1191 /*
1192  * Convert a model/commodity pair into a gtk_tree_model_iter. This
1193  * routine should only be called from the file
1194  * gnc-tree-view-price.c.
1195  */
1196 gboolean
1198  gnc_commodity *commodity,
1199  GtkTreeIter *iter)
1200 {
1201  gnc_commodity_namespace *name_space;
1202  GList *list;
1203  gint n;
1204 
1205  ENTER("model %p, commodity %p, iter %p", model, commodity, iter);
1206  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1207  g_return_val_if_fail ((commodity != NULL), FALSE);
1208  g_return_val_if_fail ((iter != NULL), FALSE);
1209 
1210  name_space = gnc_commodity_get_namespace_ds(commodity);
1211  if (name_space == NULL)
1212  {
1213  LEAVE("no namespace");
1214  return FALSE;
1215  }
1216 
1217  list = gnc_commodity_namespace_get_commodity_list(name_space);
1218  if (list == NULL)
1219  {
1220  LEAVE("empty list");
1221  return FALSE;
1222  }
1223 
1224  n = g_list_index(list, commodity);
1225  g_list_free (list);
1226  if (n == -1)
1227  {
1228  LEAVE("commodity not in list");
1229  return FALSE;
1230  }
1231 
1232  iter->stamp = model->stamp;
1233  iter->user_data = ITER_IS_COMMODITY;
1234  iter->user_data2 = commodity;
1235  iter->user_data3 = GINT_TO_POINTER(n);
1236  LEAVE("iter %s", iter_to_string(model, iter));
1237  return TRUE;
1238 }
1239 
1240 /*
1241  * Convert a model/namespace pair into a gtk_tree_model_iter. This
1242  * routine should only be called from the file
1243  * gnc-tree-view-price.c.
1244  */
1245 gboolean
1247  gnc_commodity_namespace *name_space,
1248  GtkTreeIter *iter)
1249 {
1250  gnc_commodity_table *ct;
1251  GList *list;
1252  gint n;
1253 
1254  ENTER("model %p, namespace %p, iter %p", model, name_space, iter);
1255  g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
1256  g_return_val_if_fail ((name_space != NULL), FALSE);
1257  g_return_val_if_fail ((iter != NULL), FALSE);
1258 
1259  ct = qof_book_get_data (model->book, GNC_COMMODITY_TABLE);
1261  if (list == NULL)
1262  {
1263  LEAVE("namespace list empty");
1264  return FALSE;
1265  }
1266 
1267  n = g_list_index(list, name_space);
1268  g_list_free (list);
1269  if (n == -1)
1270  {
1271  LEAVE("namespace not found");
1272  return FALSE;
1273  }
1274 
1275  iter->stamp = model->stamp;
1276  iter->user_data = ITER_IS_NAMESPACE;
1277  iter->user_data2 = name_space;
1278  iter->user_data3 = GINT_TO_POINTER(n);
1279  LEAVE("iter %s", iter_to_string(model, iter));
1280  return TRUE;
1281 }
1282 
1283 /************************************************************/
1284 /* Price Tree Model - Engine Event Handling Functions */
1285 /************************************************************/
1286 
1287 typedef struct _remove_data
1288 {
1289  GncTreeModelPrice *model;
1290  GtkTreePath *path;
1291 } remove_data;
1292 
1293 static GSList *pending_removals = NULL;
1294 
1306 static void
1307 gnc_tree_model_price_row_add (GncTreeModelPrice *model,
1308  GtkTreeIter *iter)
1309 {
1310  GtkTreePath *path;
1311  GtkTreeModel *tree_model;
1312  GtkTreeIter tmp_iter;
1313 
1314  ENTER("model %p, iter (%p)%s", model, iter, iter_to_string(model, iter));
1315 
1316  /* We're adding a row, so the lists on which this model is based have
1317  * changed. Since existing iterators (except the one just passed in)
1318  * are all based on old indexes into those lists, we need to invalidate
1319  * them, which we can do by changing the model's stamp. */
1320  do
1321  {
1322  model->stamp++;
1323  }
1324  while (model->stamp == 0);
1325  iter->stamp = model->stamp;
1326 
1327  /* Tag the new row as inserted. */
1328  tree_model = GTK_TREE_MODEL(model);
1329  path = gnc_tree_model_price_get_path (tree_model, iter);
1330  gtk_tree_model_row_inserted (tree_model, path, iter);
1331 
1332  /* Inform all ancestors. */
1333  /*
1334  * Charles Day: I don't think calls to gtk_tree_model_row_changed() should
1335  * be necessary. It is just a workaround for bug #540201.
1336  */
1337  if (gtk_tree_path_up(path) &&
1338  gtk_tree_path_get_depth(path) > 0 &&
1339  gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
1340  {
1341  /* Signal the change to the parent. */
1342  gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
1343 
1344  /* Is this the parent's first child? */
1345  if (gtk_tree_model_iter_n_children(tree_model, &tmp_iter) == 1)
1346  gtk_tree_model_row_has_child_toggled(tree_model, path, &tmp_iter);
1347 
1348  /* Signal any other ancestors. */
1349  while (gtk_tree_path_up(path) &&
1350  gtk_tree_path_get_depth(path) > 0 &&
1351  gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
1352  {
1353  gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
1354  }
1355  }
1356  gtk_tree_path_free(path);
1357 
1358  /* If the new row already has children, signal that so the expander
1359  * can be shown. This can happen, for example, if a namespace or
1360  * commodity is changed in another place (like the Security Editor)
1361  * and gets removed and then re-added to the commodity db. */
1362  if (gnc_tree_model_price_iter_has_child(tree_model, iter))
1363  {
1364  path = gnc_tree_model_price_get_path(tree_model, iter);
1365  gtk_tree_model_row_has_child_toggled(tree_model, path, iter);
1366  gtk_tree_path_free(path);
1367  }
1368 
1369  LEAVE(" ");
1370 }
1371 
1383 static void
1384 gnc_tree_model_price_row_delete (GncTreeModelPrice *model,
1385  GtkTreePath *path)
1386 {
1387  GtkTreeModel *tree_model;
1388  GtkTreeIter iter;
1389 
1390  g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
1391  g_return_if_fail(path);
1392 
1393  debug_path(ENTER, path);
1394 
1395  tree_model = GTK_TREE_MODEL(model);
1396 
1397  /* We're removing a row, so the lists on which this model is based have
1398  * changed. Since existing iterators are all based on old indexes into
1399  * those lists, we need to invalidate them, which we can do by changing
1400  * the model's stamp. */
1401  do
1402  {
1403  model->stamp++;
1404  }
1405  while (model->stamp == 0);
1406 
1407  /* Signal that the path has been deleted. */
1408  gtk_tree_model_row_deleted(tree_model, path);
1409 
1410  /* Issue any appropriate signals to ancestors. */
1411  if (gtk_tree_path_up(path) &&
1412  gtk_tree_path_get_depth(path) > 0 &&
1413  gtk_tree_model_get_iter(tree_model, &iter, path))
1414  {
1415  DEBUG("iter %s", iter_to_string(model, &iter));
1416 
1417  /* Signal the change to the parent. */
1418  gtk_tree_model_row_changed(tree_model, path, &iter);
1419 
1420  /* Was this the parent's only child? */
1421  if (!gtk_tree_model_iter_has_child(tree_model, &iter))
1422  gtk_tree_model_row_has_child_toggled(tree_model, path, &iter);
1423 
1424  /* Signal any other ancestors. */
1425  while (gtk_tree_path_up(path) &&
1426  gtk_tree_path_get_depth(path) > 0 &&
1427  gtk_tree_model_get_iter(tree_model, &iter, path))
1428  {
1429  DEBUG("iter %s", iter_to_string(model, &iter));
1430  gtk_tree_model_row_changed(tree_model, path, &iter);
1431  }
1432  }
1433 
1434  LEAVE(" ");
1435 }
1436 
1437 
1454 static gboolean
1455 gnc_tree_model_price_do_deletions (gpointer price_db)
1456 {
1457  ENTER(" ");
1458 
1459  /* Go through the list of paths needing removal. */
1460  while (pending_removals)
1461  {
1462  remove_data *data = pending_removals->data;
1463  pending_removals = g_slist_delete_link(pending_removals, pending_removals);
1464 
1465  if (data)
1466  {
1467  debug_path(DEBUG, data->path);
1468 
1469  /* Remove the path. */
1470  gnc_tree_model_price_row_delete(data->model, data->path);
1471  gnc_pricedb_nth_price_reset_cache (price_db);
1472 
1473  gtk_tree_path_free(data->path);
1474  g_free(data);
1475  }
1476  }
1477 
1478  LEAVE(" ");
1479  /* Don't call me again. */
1480  return FALSE;
1481 }
1482 
1483 
1515 static void
1516 gnc_tree_model_price_event_handler (QofInstance *entity,
1517  QofEventId event_type,
1518  gpointer user_data,
1519  gpointer event_data)
1520 {
1521  GncTreeModelPrice *model;
1522  GtkTreePath *path;
1523  GtkTreeIter iter;
1524  remove_data *data;
1525  const gchar *name;
1526 
1527  ENTER("entity %p, event %d, model %p, event data %p",
1528  entity, event_type, user_data, event_data);
1529  model = (GncTreeModelPrice *)user_data;
1530 
1531  /* Do deletions if any are pending. */
1532  if (pending_removals)
1533  gnc_tree_model_price_do_deletions (model->price_db);
1534 
1535  /* hard failures */
1536  g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
1537 
1538  /* get type specific data */
1539  if (GNC_IS_COMMODITY(entity))
1540  {
1541  gnc_commodity *commodity;
1542 
1543  commodity = GNC_COMMODITY(entity);
1544  name = gnc_commodity_get_mnemonic(commodity);
1545  if (event_type != QOF_EVENT_DESTROY)
1546  {
1547  if (!gnc_tree_model_price_get_iter_from_commodity (model, commodity, &iter))
1548  {
1549  LEAVE("no iter");
1550  return;
1551  }
1552  }
1553  }
1554  else if (GNC_IS_COMMODITY_NAMESPACE(entity))
1555  {
1556  gnc_commodity_namespace *name_space;
1557 
1558  name_space = GNC_COMMODITY_NAMESPACE(entity);
1559  name = gnc_commodity_namespace_get_name(name_space);
1560  if (event_type != QOF_EVENT_DESTROY)
1561  {
1562  if (!gnc_tree_model_price_get_iter_from_namespace (model, name_space, &iter))
1563  {
1564  LEAVE("no iter");
1565  return;
1566  }
1567  }
1568  }
1569  else if (GNC_IS_PRICE(entity))
1570  {
1571  GNCPrice *price;
1572 
1573  price = GNC_PRICE(entity);
1574  name = "price";
1575  if (event_type != QOF_EVENT_DESTROY)
1576  {
1577  if (!gnc_tree_model_price_get_iter_from_price (model, price, &iter))
1578  {
1579  LEAVE("no iter");
1580  return;
1581  }
1582  }
1583  }
1584  else
1585  {
1586  LEAVE(" ");
1587  return;
1588  }
1589 
1590  switch (event_type)
1591  {
1592  case QOF_EVENT_ADD:
1593  /* Tell the filters/views where the new price was added. */
1594  DEBUG("add %s", name);
1595  gnc_pricedb_nth_price_reset_cache (model->price_db);
1596  gnc_tree_model_price_row_add (model, &iter);
1597  break;
1598 
1599  case QOF_EVENT_REMOVE:
1600  /* Record the path of this account for later use in destruction */
1601  DEBUG("remove %s", name);
1602  path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
1603  if (path == NULL)
1604  {
1605  LEAVE("not in model");
1606  return;
1607  }
1608 
1609  data = g_new0 (remove_data, 1);
1610  data->model = model;
1611  data->path = path;
1612  pending_removals = g_slist_append (pending_removals, data);
1613  g_idle_add_full(G_PRIORITY_HIGH_IDLE,
1614  gnc_tree_model_price_do_deletions, model->price_db, NULL);
1615 
1616  LEAVE(" ");
1617  return;
1618 
1619  case QOF_EVENT_MODIFY:
1620  DEBUG("change %s", name);
1621  path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
1622  if (path == NULL)
1623  {
1624  LEAVE("not in model");
1625  return;
1626  }
1627  if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &iter, path))
1628  {
1629  gtk_tree_path_free(path);
1630  LEAVE("can't find iter for path");
1631  return;
1632  }
1633  gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter);
1634  gtk_tree_path_free(path);
1635  LEAVE(" ");
1636  return;
1637 
1638  default:
1639  LEAVE("ignored event for %s", name);
1640  return;
1641  }
1642  LEAVE(" new stamp %u", model->stamp);
1643 }
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:573
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.