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