GnuCash c935c2f+
Loading...
Searching...
No Matches
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"
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
82static QofLogModule log_module = GNC_MOD_GUI;
83
85static void gnc_tree_model_price_finalize (GObject *object);
86static void gnc_tree_model_price_dispose (GObject *object);
87
88static void gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface);
89static guint gnc_tree_model_price_get_flags (GtkTreeModel *tree_model);
90static int gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model);
91static GType gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
92 int index);
93static gboolean gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
94 GtkTreeIter *iter,
95 GtkTreePath *path);
96static GtkTreePath *gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
97 GtkTreeIter *iter);
98static void gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
99 GtkTreeIter *iter,
100 int column,
101 GValue *value);
102static gboolean gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
103 GtkTreeIter *iter);
104static gboolean gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
105 GtkTreeIter *iter,
106 GtkTreeIter *parent);
107static gboolean gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
108 GtkTreeIter *iter);
109static int gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
110 GtkTreeIter *iter);
111static gboolean gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
112 GtkTreeIter *iter,
113 GtkTreeIter *parent,
114 int n);
115static gboolean gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
116 GtkTreeIter *iter,
117 GtkTreeIter *child);
118static 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
135G_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
139static void
140gnc_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
148static void
149gnc_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
159static void
160gnc_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
178static void
179gnc_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
199GtkTreeModel *
200gnc_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
231gboolean
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
243gboolean
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
255gboolean
256gnc_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
267gnc_commodity_namespace *
268gnc_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
281gnc_commodity *
282gnc_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
295GNCPrice *
296gnc_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
321static const gchar *
322iter_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
392static void
393gnc_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
409static guint
410gnc_tree_model_price_get_flags (GtkTreeModel *tree_model)
411{
412 return 0;
413}
414
415static int
416gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model)
417{
418 return GNC_TREE_MODEL_PRICE_NUM_COLUMNS;
419}
420
421static GType
422gnc_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
445static gboolean
446gnc_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
552static GtkTreePath *
553gnc_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
617static void
618gnc_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
744static gboolean
745gnc_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
814static gboolean
815gnc_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
891static gboolean
892gnc_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
935static int
936gnc_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
984static gboolean
985gnc_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;
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
1048static gboolean
1049gnc_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);
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 */
1110gboolean
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 {
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);
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 */
1160GtkTreePath *
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 */
1196gboolean
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
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 */
1245gboolean
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
1287typedef struct _remove_data
1288{
1289 GncTreeModelPrice *model;
1290 GtkTreePath *path;
1291} remove_data;
1292
1293static GSList *pending_removals = NULL;
1294
1306static void
1307gnc_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
1383static void
1384gnc_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
1454static gboolean
1455gnc_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
1515static void
1516gnc_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}
All type declarations for the whole Gnucash engine.
Gobject helper routines.
a simple price database for gnucash
GtkTreeModel implementation for gnucash price database.
utility functions for the GnuCash UI
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the 'print' name for the specified commodity.
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
gnc_commodity_namespace * gnc_commodity_get_namespace_ds(const gnc_commodity *cm)
Retrieve the namespace data structure for the specified commodity.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
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.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition gnc-date.h:108
size_t qof_print_date_buff(char *buff, const size_t len, time64 t)
Convenience: calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:574
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition qofevent.cpp:103
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition qofevent.cpp:73
gint QofEventId
Define the type of events allowed.
Definition qofevent.h:45
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
gboolean gnc_tree_model_price_get_iter_from_commodity(GncTreeModelPrice *model, gnc_commodity *commodity, GtkTreeIter *iter)
Convert a commodity pointer into a GtkTreeIter.
gnc_commodity_namespace * gnc_tree_model_price_get_namespace(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash commodity namespace.
GtkTreePath * gnc_tree_model_price_get_path_from_price(GncTreeModelPrice *model, GNCPrice *price)
Convert a price pointer into a GtkTreePath.
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.
GtkTreeModel * gnc_tree_model_price_new(QofBook *book, GNCPriceDB *price_db)
Create a new GtkTreeModel for manipulating gnucash commodity prices.
gboolean gnc_tree_model_price_iter_is_commodity(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a commodity.
gboolean gnc_tree_model_price_iter_is_price(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a price.
gnc_commodity * gnc_tree_model_price_get_commodity(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash commodity.
GNCPrice * gnc_tree_model_price_get_price(GncTreeModelPrice *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash price.
gboolean gnc_tree_model_price_iter_is_namespace(GncTreeModelPrice *model, GtkTreeIter *iter)
Determine whether or not the specified GtkTreeIter points to a "commodity namespace".
gboolean gnc_tree_model_price_get_iter_from_price(GncTreeModelPrice *model, GNCPrice *price, GtkTreeIter *iter)
Convert a price pointer into a GtkTreeIter.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
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.
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.
int gnc_pricedb_num_prices(GNCPriceDB *db, const gnc_commodity *c)
Get the number of prices, in any currency, for a given commodity.
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.
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...
QofBook reference.
Definition qofbook-p.hpp:47
The instance data structure for a price tree model.
int stamp
The state of the model.
GncTreeModel gnc_tree_model
The parent object data.