GnuCash 2.4.99
gnc-tree-model-price.c
00001 /*
00002  * gnc-tree-model-price.c -- GtkTreeModel implementation to display
00003  *      prices in a GtkTreeView.
00004  *
00005  * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
00006  * Copyright (C) 2003 David Hampton <hampton@employees.org>
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License as
00010  * published by the Free Software Foundation; either version 2 of
00011  * the License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, contact:
00020  *
00021  * Free Software Foundation           Voice:  +1-617-542-5942
00022  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
00023  * Boston, MA  02110-1301,  USA       gnu@gnu.org
00024  */
00025 
00026 /*
00027  * In this model, valid paths take the form "X", "X:Y", or "X:Y:Z", where:
00028  *   X is an index into the namespaces list held by the commodity db
00029  *   Y is an index into the commodity list for the namespace
00030  *   Z is an index into the price list for the commodity
00031  *
00032  * Iterators are populated with the following private data:
00033  *   iter->user_data   Type NAMESPACE | COMMODITY | PRICE
00034  *   iter->user_data2  A pointer to the namespace|commodity|item structure
00035  *   iter->user_data3  The index of the item within its parent list
00036  */
00037 
00038 #include "config.h"
00039 
00040 #include <gtk/gtk.h>
00041 #include <glib/gi18n.h>
00042 #include <string.h>
00043 
00044 #include "gnc-component-manager.h"
00045 #include "gnc-engine.h"
00046 #include "gnc-gobject-utils.h"
00047 #include "gnc-pricedb.h"
00048 #include "gnc-tree-model-price.h"
00049 #include "gnc-ui-util.h"
00050 
00051 #define ITER_IS_NAMESPACE GINT_TO_POINTER(1)
00052 #define ITER_IS_COMMODITY GINT_TO_POINTER(2)
00053 #define ITER_IS_PRICE     GINT_TO_POINTER(3)
00054 
00055 /*
00056  * There's a race condition in this code where a redraw of the tree
00057  * view widget gets in between the two phases of removing a GNCPrice
00058  * from the model.  I tried bumping the priority of the idle function
00059  * by 100, which should have put it well above the priority of GTK's
00060  * redraw function, but the race condition persisted.  I also tried
00061  * eliminating the second phase of the removal, but that screws up the
00062  * view filter (which lives above this code and therefore there's no
00063  * way to access it) and causes other problems.  The workaround is to
00064  * accept a tree path that points to a NULL price, and simply return
00065  * the null string to be printed by the view.  The removal kicks in
00066  * immediately after the redraw and causes the blank line to be
00067  * removed.
00068  *
00069  * Charles Day: I found that by the time the main loop is reached and
00070  * the idle timer goes off, many qof events may have been generated and
00071  * handled. In particular, a commodity could be removed, edited, and
00072  * re-added by the security editor and all those events would happen
00073  * before the timer goes off. This caused a problem where a re-added
00074  * commodity would get whacked when the timer went off. I found that
00075  * adding a check for pending removals at the beginning of the event
00076  * handler fixes that problem and also resolves the race condition.
00077  *
00078  */
00079 #define RACE_CONDITION_SOLVED
00080 
00082 static QofLogModule log_module = GNC_MOD_GUI;
00083 
00085 static void gnc_tree_model_price_class_init (GncTreeModelPriceClass *klass);
00086 static void gnc_tree_model_price_init (GncTreeModelPrice *model);
00087 static void gnc_tree_model_price_finalize (GObject *object);
00088 static void gnc_tree_model_price_dispose (GObject *object);
00089 
00090 static void gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface);
00091 static guint gnc_tree_model_price_get_flags (GtkTreeModel *tree_model);
00092 static int gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model);
00093 static GType gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
00094         int index);
00095 static gboolean gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
00096         GtkTreeIter *iter,
00097         GtkTreePath *path);
00098 static GtkTreePath *gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
00099         GtkTreeIter *iter);
00100 static void gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
00101         GtkTreeIter *iter,
00102         int column,
00103         GValue *value);
00104 static gboolean gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
00105         GtkTreeIter *iter);
00106 static gboolean gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
00107         GtkTreeIter *iter,
00108         GtkTreeIter *parent);
00109 static gboolean gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
00110         GtkTreeIter *iter);
00111 static int gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
00112         GtkTreeIter *iter);
00113 static gboolean gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
00114         GtkTreeIter *iter,
00115         GtkTreeIter *parent,
00116         int n);
00117 static gboolean gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
00118         GtkTreeIter *iter,
00119         GtkTreeIter *child);
00120 static void gnc_tree_model_price_event_handler (QofInstance *entity,
00121         QofEventId event_type,
00122         gpointer user_data,
00123         gpointer event_data);
00124 
00126 typedef struct GncTreeModelPricePrivate
00127 {
00128     QofBook *book;
00129     GNCPriceDB *price_db;
00130     gint event_handler_id;
00131     GNCPrintAmountInfo print_info;
00132 } GncTreeModelPricePrivate;
00133 
00134 #define GNC_TREE_MODEL_PRICE_GET_PRIVATE(o)  \
00135    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_TREE_MODEL_PRICE, GncTreeModelPricePrivate))
00136 
00138 static GObjectClass *parent_class = NULL;
00139 
00140 GType
00141 gnc_tree_model_price_get_type (void)
00142 {
00143     static GType gnc_tree_model_price_type = 0;
00144 
00145     if (gnc_tree_model_price_type == 0)
00146     {
00147         static const GTypeInfo our_info =
00148         {
00149             sizeof (GncTreeModelPriceClass),
00150             NULL,
00151             NULL,
00152             (GClassInitFunc) gnc_tree_model_price_class_init,
00153             NULL,
00154             NULL,
00155             sizeof (GncTreeModelPrice),
00156             0,
00157             (GInstanceInitFunc) gnc_tree_model_price_init
00158         };
00159 
00160         static const GInterfaceInfo tree_model_info =
00161         {
00162             (GInterfaceInitFunc) gnc_tree_model_price_tree_model_init,
00163             NULL,
00164             NULL
00165         };
00166 
00167         gnc_tree_model_price_type = g_type_register_static (GNC_TYPE_TREE_MODEL,
00168                                     GNC_TREE_MODEL_PRICE_NAME,
00169                                     &our_info, 0);
00170 
00171         g_type_add_interface_static (gnc_tree_model_price_type,
00172                                      GTK_TYPE_TREE_MODEL,
00173                                      &tree_model_info);
00174     }
00175 
00176     return gnc_tree_model_price_type;
00177 }
00178 
00179 static void
00180 gnc_tree_model_price_class_init (GncTreeModelPriceClass *klass)
00181 {
00182     GObjectClass *o_class = G_OBJECT_CLASS (klass);
00183 
00184     parent_class = g_type_class_peek_parent (klass);
00185 
00186     o_class->finalize = gnc_tree_model_price_finalize;
00187     o_class->dispose = gnc_tree_model_price_dispose;
00188 
00189     g_type_class_add_private(klass, sizeof(GncTreeModelPricePrivate));
00190 }
00191 
00192 static void
00193 gnc_tree_model_price_init (GncTreeModelPrice *model)
00194 {
00195     GncTreeModelPricePrivate *priv;
00196 
00197     while (model->stamp == 0)
00198     {
00199         model->stamp = g_random_int ();
00200     }
00201 
00202     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00203     priv->print_info = gnc_share_print_info_places(6);
00204 }
00205 
00206 static void
00207 gnc_tree_model_price_finalize (GObject *object)
00208 {
00209     GncTreeModelPrice *model;
00210     GncTreeModelPricePrivate *priv;
00211 
00212     ENTER("model %p", object);
00213 
00214     g_return_if_fail (object != NULL);
00215     g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (object));
00216 
00217     model = GNC_TREE_MODEL_PRICE (object);
00218     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00219 
00220     priv->book = NULL;
00221     priv->price_db = NULL;
00222 
00223     G_OBJECT_CLASS (parent_class)->finalize (object);
00224     LEAVE(" ");
00225 }
00226 
00227 static void
00228 gnc_tree_model_price_dispose (GObject *object)
00229 {
00230     GncTreeModelPrice *model;
00231     GncTreeModelPricePrivate *priv;
00232 
00233     ENTER("model %p", object);
00234     g_return_if_fail (object != NULL);
00235     g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (object));
00236 
00237     model = GNC_TREE_MODEL_PRICE (object);
00238     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00239 
00240     if (priv->event_handler_id)
00241     {
00242         qof_event_unregister_handler (priv->event_handler_id);
00243         priv->event_handler_id = 0;
00244     }
00245 
00246     if (G_OBJECT_CLASS (parent_class)->dispose)
00247         G_OBJECT_CLASS (parent_class)->dispose (object);
00248     LEAVE(" ");
00249 }
00250 
00251 GtkTreeModel *
00252 gnc_tree_model_price_new (QofBook *book, GNCPriceDB *price_db)
00253 {
00254     GncTreeModelPrice *model;
00255     GncTreeModelPricePrivate *priv;
00256     const GList *item;
00257 
00258     item = gnc_gobject_tracking_get_list(GNC_TREE_MODEL_PRICE_NAME);
00259     for ( ; item; item = g_list_next(item))
00260     {
00261         model = (GncTreeModelPrice *)item->data;
00262         priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00263         if (priv->price_db == price_db)
00264         {
00265             g_object_ref(G_OBJECT(model));
00266             LEAVE("returning existing model %p", model);
00267             return GTK_TREE_MODEL(model);
00268         }
00269     }
00270 
00271     model = g_object_new (GNC_TYPE_TREE_MODEL_PRICE,
00272                           NULL);
00273 
00274     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00275     priv->book = book;
00276     priv->price_db = price_db;
00277 
00278     priv->event_handler_id =
00279         qof_event_register_handler (gnc_tree_model_price_event_handler, model);
00280 
00281     return GTK_TREE_MODEL (model);
00282 }
00283 
00284 gboolean
00285 gnc_tree_model_price_iter_is_namespace (GncTreeModelPrice *model,
00286                                         GtkTreeIter *iter)
00287 {
00288     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
00289     g_return_val_if_fail (iter != NULL, FALSE);
00290     g_return_val_if_fail (iter->user_data != NULL, FALSE);
00291     g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
00292 
00293     return (iter->user_data == ITER_IS_NAMESPACE);
00294 }
00295 
00296 gboolean
00297 gnc_tree_model_price_iter_is_commodity (GncTreeModelPrice *model,
00298                                         GtkTreeIter *iter)
00299 {
00300     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
00301     g_return_val_if_fail (iter != NULL, FALSE);
00302     g_return_val_if_fail (iter->user_data != NULL, FALSE);
00303     g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
00304 
00305     return (iter->user_data == ITER_IS_COMMODITY);
00306 }
00307 
00308 gboolean
00309 gnc_tree_model_price_iter_is_price (GncTreeModelPrice *model,
00310                                     GtkTreeIter *iter)
00311 {
00312     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
00313     g_return_val_if_fail (iter != NULL, FALSE);
00314     g_return_val_if_fail (iter->user_data != NULL, FALSE);
00315     g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
00316 
00317     return (iter->user_data == ITER_IS_PRICE);
00318 }
00319 
00320 gnc_commodity_namespace *
00321 gnc_tree_model_price_get_namespace (GncTreeModelPrice *model,
00322                                     GtkTreeIter *iter)
00323 {
00324     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
00325     g_return_val_if_fail (iter != NULL, NULL);
00326     g_return_val_if_fail (iter->user_data != NULL, NULL);
00327     g_return_val_if_fail (iter->stamp == model->stamp, NULL);
00328 
00329     if (iter->user_data != ITER_IS_NAMESPACE)
00330         return NULL;
00331     return (gnc_commodity_namespace *)iter->user_data2;
00332 }
00333 
00334 gnc_commodity *
00335 gnc_tree_model_price_get_commodity (GncTreeModelPrice *model,
00336                                     GtkTreeIter *iter)
00337 {
00338     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
00339     g_return_val_if_fail (iter != NULL, NULL);
00340     g_return_val_if_fail (iter->user_data != NULL, NULL);
00341     g_return_val_if_fail (iter->stamp == model->stamp, NULL);
00342 
00343     if (iter->user_data != ITER_IS_COMMODITY)
00344         return NULL;
00345     return (gnc_commodity *)iter->user_data2;
00346 }
00347 
00348 GNCPrice *
00349 gnc_tree_model_price_get_price (GncTreeModelPrice *model,
00350                                 GtkTreeIter *iter)
00351 {
00352     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
00353     g_return_val_if_fail (iter != NULL, NULL);
00354     g_return_val_if_fail (iter->user_data != NULL, NULL);
00355     g_return_val_if_fail (iter->stamp == model->stamp, NULL);
00356 
00357     if (iter->user_data != ITER_IS_PRICE)
00358         return NULL;
00359     return (GNCPrice *)iter->user_data2;
00360 }
00361 
00362 /************************************************************/
00363 /*        Gnc Tree Model Debugging Utility Function         */
00364 /************************************************************/
00365 
00366 #define debug_path(fn, path) {                                  \
00367     gchar *path_string = gtk_tree_path_to_string(path);         \
00368     fn("tree path %s", path_string? path_string : "(NULL)");    \
00369     g_free(path_string);                                        \
00370   }
00371 
00372 #define ITER_STRING_LEN 256
00373 
00374 static const gchar *
00375 iter_to_string (GncTreeModelPrice *model, GtkTreeIter *iter)
00376 {
00377     GncTreeModelPricePrivate *priv;
00378     gnc_commodity_namespace *namespace;
00379     gnc_commodity *commodity;
00380     GNCPrice *price;
00381 #ifdef G_THREADS_ENABLED
00382     static GStaticPrivate gtmits_buffer_key = G_STATIC_PRIVATE_INIT;
00383     gchar *string;
00384 
00385     string = g_static_private_get (&gtmits_buffer_key);
00386     if (string == NULL)
00387     {
00388         string = g_malloc(ITER_STRING_LEN + 1);
00389         g_static_private_set (&gtmits_buffer_key, string, g_free);
00390     }
00391 #else
00392     static char string[ITER_STRING_LEN + 1];
00393 #endif
00394 
00395     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00396     if (iter)
00397     {
00398         switch (GPOINTER_TO_INT(iter->user_data))
00399         {
00400         case GPOINTER_TO_INT(ITER_IS_NAMESPACE):
00401             namespace = (gnc_commodity_namespace *) iter->user_data2;
00402             snprintf(string, ITER_STRING_LEN,
00403                      "[stamp:%x data:%d (NAMESPACE), %p (%s), %d]",
00404                      iter->stamp, GPOINTER_TO_INT(iter->user_data),
00405                      iter->user_data2, gnc_commodity_namespace_get_name (namespace),
00406                      GPOINTER_TO_INT(iter->user_data3));
00407             break;
00408 
00409         case GPOINTER_TO_INT(ITER_IS_COMMODITY):
00410             commodity = (gnc_commodity *) iter->user_data2;
00411             snprintf(string, ITER_STRING_LEN,
00412                      "[stamp:%x data:%d (COMMODITY), %p (%s), %d]",
00413                      iter->stamp, GPOINTER_TO_INT(iter->user_data),
00414                      iter->user_data2, gnc_commodity_get_mnemonic (commodity),
00415                      GPOINTER_TO_INT(iter->user_data3));
00416             break;
00417 
00418         case GPOINTER_TO_INT(ITER_IS_PRICE):
00419             price = (GNCPrice *) iter->user_data2;
00420             commodity = gnc_price_get_commodity(price);
00421             snprintf(string, ITER_STRING_LEN,
00422                      "[stamp:%x data:%d (PRICE), %p (%s:%s), %d]",
00423                      iter->stamp, GPOINTER_TO_INT(iter->user_data),
00424                      iter->user_data2, gnc_commodity_get_mnemonic (commodity),
00425                      xaccPrintAmount (gnc_price_get_value (price), priv->print_info),
00426                      GPOINTER_TO_INT(iter->user_data3));
00427             break;
00428 
00429         default:
00430             snprintf(string, ITER_STRING_LEN,
00431                      "[stamp:%x data:%d (UNKNOWN), %p, %d]",
00432                      iter->stamp,
00433                      GPOINTER_TO_INT(iter->user_data),
00434                      iter->user_data2,
00435                      GPOINTER_TO_INT(iter->user_data3));
00436             break;
00437         }
00438     }
00439     return string;
00440 }
00441 
00442 
00443 /************************************************************/
00444 /*       Gtk Tree Model Required Interface Functions        */
00445 /************************************************************/
00446 
00447 static void
00448 gnc_tree_model_price_tree_model_init (GtkTreeModelIface *iface)
00449 {
00450     iface->get_flags       = gnc_tree_model_price_get_flags;
00451     iface->get_n_columns   = gnc_tree_model_price_get_n_columns;
00452     iface->get_column_type = gnc_tree_model_price_get_column_type;
00453     iface->get_iter        = gnc_tree_model_price_get_iter;
00454     iface->get_path        = gnc_tree_model_price_get_path;
00455     iface->get_value       = gnc_tree_model_price_get_value;
00456     iface->iter_next       = gnc_tree_model_price_iter_next;
00457     iface->iter_children   = gnc_tree_model_price_iter_children;
00458     iface->iter_has_child  = gnc_tree_model_price_iter_has_child;
00459     iface->iter_n_children = gnc_tree_model_price_iter_n_children;
00460     iface->iter_nth_child  = gnc_tree_model_price_iter_nth_child;
00461     iface->iter_parent     = gnc_tree_model_price_iter_parent;
00462 }
00463 
00464 static guint
00465 gnc_tree_model_price_get_flags (GtkTreeModel *tree_model)
00466 {
00467     return 0;
00468 }
00469 
00470 static int
00471 gnc_tree_model_price_get_n_columns (GtkTreeModel *tree_model)
00472 {
00473     return GNC_TREE_MODEL_PRICE_NUM_COLUMNS;
00474 }
00475 
00476 static GType
00477 gnc_tree_model_price_get_column_type (GtkTreeModel *tree_model,
00478                                       int index)
00479 {
00480     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), G_TYPE_INVALID);
00481     g_return_val_if_fail ((index < GNC_TREE_MODEL_PRICE_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID);
00482 
00483     switch (index)
00484     {
00485     case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
00486     case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
00487     case GNC_TREE_MODEL_PRICE_COL_DATE:
00488     case GNC_TREE_MODEL_PRICE_COL_SOURCE:
00489     case GNC_TREE_MODEL_PRICE_COL_TYPE:
00490     case GNC_TREE_MODEL_PRICE_COL_VALUE:
00491         return G_TYPE_STRING;
00492     case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
00493         return G_TYPE_BOOLEAN;
00494     default:
00495         g_assert_not_reached ();
00496         return G_TYPE_INVALID;
00497     }
00498 }
00499 
00500 static gboolean
00501 gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
00502                                GtkTreeIter *iter,
00503                                GtkTreePath *path)
00504 {
00505     GncTreeModelPrice *model;
00506     GncTreeModelPricePrivate *priv;
00507     gnc_commodity_table *ct;
00508     gnc_commodity_namespace *namespace;
00509     gnc_commodity *commodity = NULL;
00510     GNCPrice *price;
00511     GList *ns_list, *cm_list, *price_list;
00512     guint i, depth;
00513 
00514     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
00515 
00516     depth = gtk_tree_path_get_depth (path);
00517     ENTER("model %p, iter %p, path %p (depth %d)", tree_model, iter, path, depth);
00518     debug_path(DEBUG, path);
00519 
00520     /* Check the path depth. */
00521     if (depth == 0)
00522     {
00523         LEAVE("depth too small");
00524         return FALSE;
00525     }
00526     if (depth > 3)
00527     {
00528         LEAVE("depth too big");
00529         return FALSE;
00530     }
00531 
00532     /* Make sure the model has a price db. */
00533     model = GNC_TREE_MODEL_PRICE (tree_model);
00534     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00535     if (priv->price_db == NULL)
00536     {
00537         LEAVE("no price db");
00538         return FALSE;
00539     }
00540 
00541     /* Verify the first part of the path: the namespace. */
00542     ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
00543     ns_list = gnc_commodity_table_get_namespaces_list(ct);
00544     i = gtk_tree_path_get_indices (path)[0];
00545     namespace = g_list_nth_data (ns_list, i);
00546     if (!namespace)
00547     {
00548         LEAVE("invalid path at namespace");
00549         return FALSE;
00550     }
00551 
00552     if (depth == 1)
00553     {
00554         /* Return an iterator for the namespace. */
00555         iter->stamp      = model->stamp;
00556         iter->user_data  = ITER_IS_NAMESPACE;
00557         iter->user_data2 = namespace;
00558         iter->user_data3 = GINT_TO_POINTER(i);
00559         LEAVE("iter (ns) %s", iter_to_string(model, iter));
00560         return TRUE;
00561     }
00562 
00563     /* Verify the second part of the path: the commodity. */
00564     cm_list = gnc_commodity_namespace_get_commodity_list(namespace);
00565     i = gtk_tree_path_get_indices (path)[1];
00566     commodity = g_list_nth_data (cm_list, i);
00567     if (!commodity)
00568     {
00569         LEAVE("invalid path at commodity");
00570         return FALSE;
00571     }
00572 
00573     if (depth == 2)
00574     {
00575         /* Return an iterator for the commodity. */
00576         iter->stamp      = model->stamp;
00577         iter->user_data  = ITER_IS_COMMODITY;
00578         iter->user_data2 = commodity;
00579         iter->user_data3 = GINT_TO_POINTER(i);
00580         LEAVE("iter (cm) %s", iter_to_string(model, iter));
00581         return TRUE;
00582     }
00583 
00584     /* Verify the third part of the path: the price. */
00585     price_list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
00586     i = gtk_tree_path_get_indices (path)[2];
00587     price = g_list_nth_data (price_list, i);
00588     gnc_price_list_destroy(price_list);
00589     /* There's a race condition here that I can't resolve.
00590      * Comment this check out for now, and we'll handle the
00591      * resulting problem elsewhere. */
00592 #ifdef RACE_CONDITION_SOLVED
00593     if (!price)
00594     {
00595         LEAVE("invalid path at price");
00596         return FALSE;
00597     }
00598 #endif
00599 
00600     /* Return an iterator for the price. */
00601     iter->stamp      = model->stamp;
00602     iter->user_data  = ITER_IS_PRICE;
00603     iter->user_data2 = price;
00604     iter->user_data3 = GINT_TO_POINTER(i);
00605     LEAVE("iter (pc) %s", iter_to_string(model, iter));
00606     return TRUE;
00607 }
00608 
00609 static GtkTreePath *
00610 gnc_tree_model_price_get_path (GtkTreeModel *tree_model,
00611                                GtkTreeIter *iter)
00612 {
00613     GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
00614     GncTreeModelPricePrivate *priv;
00615     gnc_commodity_table *ct;
00616     gnc_commodity_namespace *namespace;
00617     gnc_commodity *commodity;
00618     GList *ns_list, *cm_list;
00619     GtkTreePath *path;
00620 
00621     ENTER("model %p, iter %p (%s)", tree_model, iter, iter_to_string(model, iter));
00622     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
00623     g_return_val_if_fail (iter != NULL, NULL);
00624     g_return_val_if_fail (iter->user_data != NULL, NULL);
00625     g_return_val_if_fail (iter->stamp == model->stamp, NULL);
00626 
00627     /* Make sure this model has a price db. */
00628     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00629     if (priv->price_db == NULL)
00630     {
00631         LEAVE("no price db");
00632         return FALSE;
00633     }
00634 
00635     if (iter->user_data == ITER_IS_NAMESPACE)
00636     {
00637         /* Create a path to the namespace. This is just the index into
00638          * the namespace list, which we already stored in user_data3. */
00639         path = gtk_tree_path_new ();
00640         gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
00641         debug_path(LEAVE, path);
00642         return path;
00643     }
00644 
00645     /* Get the namespaces list. */
00646     ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
00647     ns_list = gnc_commodity_table_get_namespaces_list(ct);
00648 
00649     if (iter->user_data == ITER_IS_COMMODITY)
00650     {
00651         /* Create a path to the commodity. */
00652         commodity = (gnc_commodity*)iter->user_data2;
00653         namespace = gnc_commodity_get_namespace_ds(commodity);
00654         path = gtk_tree_path_new ();
00655         gtk_tree_path_append_index (path, g_list_index (ns_list, namespace));
00656         gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
00657         debug_path(LEAVE, path);
00658         return path;
00659     }
00660 
00661     /* Create a path to the price. */
00662     commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
00663     namespace = gnc_commodity_get_namespace_ds(commodity);
00664     cm_list = gnc_commodity_namespace_get_commodity_list(namespace);
00665     path = gtk_tree_path_new ();
00666     gtk_tree_path_append_index (path, g_list_index (ns_list, namespace));
00667     gtk_tree_path_append_index (path, g_list_index (cm_list, commodity));
00668     gtk_tree_path_append_index (path, GPOINTER_TO_INT(iter->user_data3));
00669     debug_path(LEAVE, path);
00670     return path;
00671 }
00672 
00673 static void
00674 gnc_tree_model_price_get_value (GtkTreeModel *tree_model,
00675                                 GtkTreeIter *iter,
00676                                 int column,
00677                                 GValue *value)
00678 {
00679     GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
00680     GncTreeModelPricePrivate *priv;
00681     gnc_commodity_namespace *namespace;
00682     gnc_commodity *commodity;
00683     GNCPrice *price;
00684 
00685     g_return_if_fail (GNC_IS_TREE_MODEL_PRICE (model));
00686     g_return_if_fail (iter != NULL);
00687 #ifdef RACE_CONDITION_SOLVED
00688     g_return_if_fail (iter->user_data != NULL);
00689 #endif
00690     g_return_if_fail (iter->stamp == model->stamp);
00691 
00692     if (iter->user_data == ITER_IS_NAMESPACE)
00693     {
00694         namespace = (gnc_commodity_namespace *)iter->user_data2;
00695         switch (column)
00696         {
00697         case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
00698             g_value_init (value, G_TYPE_STRING);
00699             g_value_set_string (value, gnc_commodity_namespace_get_name (namespace));
00700             break;
00701         case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
00702             g_value_init (value, G_TYPE_BOOLEAN);
00703             g_value_set_boolean (value, FALSE);
00704             break;
00705         default:
00706             g_value_init (value, G_TYPE_STRING);
00707             g_value_set_string (value, "");
00708             break;
00709         }
00710         return;
00711     }
00712 
00713     if (iter->user_data == ITER_IS_COMMODITY)
00714     {
00715         commodity = (gnc_commodity *)iter->user_data2;
00716         switch (column)
00717         {
00718         case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
00719             g_value_init (value, G_TYPE_STRING);
00720             g_value_set_string (value, gnc_commodity_get_printname (commodity));
00721             break;
00722         case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
00723             g_value_init (value, G_TYPE_BOOLEAN);
00724             g_value_set_boolean (value, FALSE);
00725             break;
00726         default:
00727             g_value_init (value, G_TYPE_STRING);
00728             g_value_set_string (value, "");
00729             break;
00730         }
00731         return;
00732     }
00733 
00734     price = (GNCPrice *)iter->user_data2;
00735 #ifdef RACE_CONDITION_SOLVED
00736     g_return_if_fail (price != NULL);
00737 #else
00738     if (price == NULL)
00739     {
00740         switch (column)
00741         {
00742         case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
00743             g_value_init (value, G_TYPE_STRING);
00744             g_value_set_string (value, "");
00745             break;
00746         case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
00747             g_value_init (value, G_TYPE_BOOLEAN);
00748             g_value_set_boolean (value, FALSE);
00749             break;
00750         default:
00751             g_value_init (value, G_TYPE_STRING);
00752             g_value_set_string (value, "");
00753             break;
00754         }
00755         return;
00756     }
00757 #endif
00758 
00759     switch (column)
00760     {
00761     case GNC_TREE_MODEL_PRICE_COL_COMMODITY:
00762         g_value_init (value, G_TYPE_STRING);
00763         commodity = gnc_price_get_commodity (price);
00764         g_value_set_string (value, gnc_commodity_get_printname (commodity));
00765         break;
00766     case GNC_TREE_MODEL_PRICE_COL_CURRENCY:
00767         g_value_init (value, G_TYPE_STRING);
00768         commodity = gnc_price_get_currency (price);
00769         g_value_set_string (value, gnc_commodity_get_printname (commodity));
00770         break;
00771     case GNC_TREE_MODEL_PRICE_COL_DATE:
00772         g_value_init (value, G_TYPE_STRING);
00773         g_value_set_string (value, gnc_print_date (gnc_price_get_time (price)));
00774         break;
00775     case GNC_TREE_MODEL_PRICE_COL_SOURCE:
00776         g_value_init (value, G_TYPE_STRING);
00777         g_value_set_string (value, gettext (gnc_price_get_source (price)));
00778         break;
00779     case GNC_TREE_MODEL_PRICE_COL_TYPE:
00780         g_value_init (value, G_TYPE_STRING);
00781         g_value_set_string (value, gnc_price_get_typestr (price));
00782         break;
00783     case GNC_TREE_MODEL_PRICE_COL_VALUE:
00784         g_value_init (value, G_TYPE_STRING);
00785         priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00786         g_value_set_string (value, xaccPrintAmount (gnc_price_get_value (price),
00787                             priv->print_info));
00788         break;
00789     case GNC_TREE_MODEL_PRICE_COL_VISIBILITY:
00790         g_value_init (value, G_TYPE_BOOLEAN);
00791         g_value_set_boolean (value, TRUE);
00792         break;
00793     default:
00794         g_assert_not_reached ();
00795     }
00796 }
00797 
00798 static gboolean
00799 gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
00800                                 GtkTreeIter *iter)
00801 {
00802     GncTreeModelPrice *model = GNC_TREE_MODEL_PRICE (tree_model);
00803     GncTreeModelPricePrivate *priv;
00804     gnc_commodity_table *ct;
00805     gnc_commodity *commodity;
00806     gnc_commodity_namespace *namespace;
00807     GList *list;
00808     gint n;
00809 
00810     ENTER("model %p, iter %p(%s)", tree_model, iter, iter_to_string(model, iter));
00811     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
00812     g_return_val_if_fail (iter != NULL, FALSE);
00813     g_return_val_if_fail (iter->user_data != NULL, FALSE);
00814     g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
00815 
00816     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00817     if (iter->user_data == ITER_IS_NAMESPACE)
00818     {
00819         ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
00820         list = gnc_commodity_table_get_namespaces_list(ct);
00821         n = GPOINTER_TO_INT(iter->user_data3) + 1;
00822         iter->user_data2 = g_list_nth_data(list, n);
00823         if (iter->user_data2 == NULL)
00824         {
00825             LEAVE("no next iter");
00826             return FALSE;
00827         }
00828         iter->user_data3 = GINT_TO_POINTER(n);
00829         LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
00830         return TRUE;
00831     }
00832     else if (iter->user_data == ITER_IS_COMMODITY)
00833     {
00834         namespace = gnc_commodity_get_namespace_ds((gnc_commodity *)iter->user_data2);
00835         list = gnc_commodity_namespace_get_commodity_list(namespace);
00836         n = GPOINTER_TO_INT(iter->user_data3) + 1;
00837         iter->user_data2 = g_list_nth_data(list, n);
00838         if (iter->user_data2 == NULL)
00839         {
00840             LEAVE("no next iter");
00841             return FALSE;
00842         }
00843         iter->user_data3 = GINT_TO_POINTER(n);
00844         LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
00845         return TRUE;
00846     }
00847     else if (iter->user_data == ITER_IS_PRICE)
00848     {
00849         commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
00850         n = GPOINTER_TO_INT(iter->user_data3) + 1;
00851         list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
00852         iter->user_data2 = g_list_nth_data(list, n);
00853         gnc_price_list_destroy(list);
00854         if (iter->user_data2 == NULL)
00855         {
00856             LEAVE("no next iter");
00857             return FALSE;
00858         }
00859         iter->user_data3 = GINT_TO_POINTER(n);
00860         LEAVE("iter %p(%s)", iter, iter_to_string(model, iter));
00861         return TRUE;
00862     }
00863     else
00864     {
00865         LEAVE("unknown iter type");
00866         return FALSE;
00867     }
00868 }
00869 
00870 static gboolean
00871 gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
00872                                     GtkTreeIter *iter,
00873                                     GtkTreeIter *parent)
00874 {
00875     GncTreeModelPrice *model;
00876     GncTreeModelPricePrivate *priv;
00877     gnc_commodity_table *ct;
00878     gnc_commodity_namespace *namespace;
00879     gnc_commodity *commodity;
00880     GList *list;
00881 
00882     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
00883 
00884     model = GNC_TREE_MODEL_PRICE (tree_model);
00885     ENTER("model %p, iter %p, parent %p (%s)",
00886           tree_model, iter, parent, iter_to_string(model, parent));
00887 
00888     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00889     if (parent == NULL)
00890     {
00891         ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
00892         list = gnc_commodity_table_get_namespaces_list(ct);
00893         if (list == NULL)
00894         {
00895             LEAVE("no namespaces");
00896             return FALSE;
00897         }
00898 
00899         iter->stamp      = model->stamp;
00900         iter->user_data  = ITER_IS_NAMESPACE;
00901         iter->user_data2 = g_list_nth_data(list, 0);
00902         iter->user_data3 = GINT_TO_POINTER(0);
00903         LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
00904         return TRUE;
00905     }
00906 
00907     if (parent->user_data == ITER_IS_NAMESPACE)
00908     {
00909         namespace = (gnc_commodity_namespace *)parent->user_data2;
00910         list = gnc_commodity_namespace_get_commodity_list(namespace);
00911         if (list == NULL)
00912         {
00913             LEAVE("no commodities");
00914             return FALSE;
00915         }
00916 
00917         iter->stamp      = model->stamp;
00918         iter->user_data  = ITER_IS_COMMODITY;
00919         iter->user_data2 = g_list_nth_data(list, 0);
00920         iter->user_data3 = GINT_TO_POINTER(0);
00921         LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
00922         return TRUE;
00923     }
00924 
00925     if (parent->user_data == ITER_IS_COMMODITY)
00926     {
00927         commodity = (gnc_commodity *)parent->user_data2;
00928         list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
00929         if (list == NULL)
00930         {
00931             LEAVE("no prices");
00932             return FALSE;
00933         }
00934         iter->stamp      = model->stamp;
00935         iter->user_data  = ITER_IS_PRICE;
00936         iter->user_data2 = g_list_nth_data(list, 0);
00937         iter->user_data3 = GINT_TO_POINTER(0);
00938         gnc_price_list_destroy(list);
00939         LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
00940         return TRUE;
00941     }
00942 
00943     LEAVE("FALSE");
00944     return FALSE;
00945 }
00946 
00947 static gboolean
00948 gnc_tree_model_price_iter_has_child (GtkTreeModel *tree_model,
00949                                      GtkTreeIter *iter)
00950 {
00951     GncTreeModelPrice *model;
00952     GncTreeModelPricePrivate *priv;
00953     gnc_commodity_namespace *namespace;
00954     gnc_commodity *commodity;
00955     gboolean result;
00956     GList *list;
00957 
00958     model = GNC_TREE_MODEL_PRICE (tree_model);
00959     ENTER("model %p, iter %p (%s)", tree_model,
00960           iter, iter_to_string(model, iter));
00961     g_return_val_if_fail (tree_model != NULL, FALSE);
00962     g_return_val_if_fail (iter != NULL, FALSE);
00963 
00964     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
00965     if (iter->user_data == ITER_IS_PRICE)
00966     {
00967         LEAVE("price has no children");
00968         return FALSE;
00969     }
00970 
00971     if (iter->user_data == ITER_IS_NAMESPACE)
00972     {
00973         namespace = (gnc_commodity_namespace *)iter->user_data2;
00974         list = gnc_commodity_namespace_get_commodity_list(namespace);
00975         LEAVE("%s children", list ? "has" : "no");
00976         return list != NULL;
00977     }
00978 
00979     if (iter->user_data == ITER_IS_COMMODITY)
00980     {
00981         commodity = (gnc_commodity *)iter->user_data2;
00982         result = gnc_pricedb_has_prices(priv->price_db, commodity, NULL);
00983         LEAVE("%s children", result ? "has" : "no");
00984         return result;
00985     }
00986 
00987     LEAVE("no children (unknown type)");
00988     return FALSE;
00989 }
00990 
00991 static int
00992 gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
00993                                       GtkTreeIter *iter)
00994 {
00995     GncTreeModelPrice *model;
00996     GncTreeModelPricePrivate *priv;
00997     gnc_commodity_table *ct;
00998     gnc_commodity_namespace *namespace;
00999     gnc_commodity *commodity;
01000     GList *list;
01001     gint n;
01002 
01003     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), -1);
01004 
01005     model = GNC_TREE_MODEL_PRICE (tree_model);
01006     ENTER("model %p, iter %p (%s)", tree_model, iter,
01007           iter_to_string(model, iter));
01008 
01009     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
01010     if (iter == NULL)
01011     {
01012         ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
01013         list = gnc_commodity_table_get_namespaces_list(ct);
01014         LEAVE("ns list length %d", g_list_length(list));
01015         return g_list_length (list);
01016     }
01017 
01018     if (iter->user_data == ITER_IS_NAMESPACE)
01019     {
01020         namespace = (gnc_commodity_namespace *)iter->user_data2;
01021         list = gnc_commodity_namespace_get_commodity_list(namespace);
01022         LEAVE("cm list length %d", g_list_length(list));
01023         return g_list_length (list);
01024     }
01025 
01026     if (iter->user_data == ITER_IS_COMMODITY)
01027     {
01028         commodity = (gnc_commodity *)iter->user_data2;
01029         list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
01030         n = g_list_length(list);
01031         gnc_price_list_destroy(list);
01032         LEAVE("price list length %d", n);
01033         return n;
01034     }
01035 
01036     LEAVE("0");
01037     return 0;
01038 }
01039 
01040 static gboolean
01041 gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
01042                                      GtkTreeIter *iter,
01043                                      GtkTreeIter *parent,
01044                                      int n)
01045 {
01046     GncTreeModelPrice *model;
01047     GncTreeModelPricePrivate *priv;
01048     gnc_commodity_table *ct;
01049     gnc_commodity_namespace *namespace;
01050     gnc_commodity *commodity;
01051     GList *list;
01052 
01053     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
01054     g_return_val_if_fail (iter != NULL, FALSE);
01055 
01056     model = GNC_TREE_MODEL_PRICE (tree_model);
01057     ENTER("model %p, iter %p, parent %p (%s), n %d",
01058           tree_model, iter, parent, iter_to_string(model, parent), n);
01059 
01060     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
01061     if (parent == NULL)
01062     {
01063         ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
01064         list = gnc_commodity_table_get_namespaces_list(ct);
01065 
01066         iter->stamp      = model->stamp;
01067         iter->user_data  = ITER_IS_NAMESPACE;
01068         iter->user_data2 = g_list_nth_data(list, n);
01069         iter->user_data3 = GINT_TO_POINTER(n);
01070         LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
01071         return iter->user_data2 != NULL;
01072     }
01073 
01074     if (parent->user_data == ITER_IS_NAMESPACE)
01075     {
01076         namespace = (gnc_commodity_namespace *)parent->user_data2;
01077         list = gnc_commodity_namespace_get_commodity_list(namespace);
01078 
01079         iter->stamp      = model->stamp;
01080         iter->user_data  = ITER_IS_COMMODITY;
01081         iter->user_data2 = g_list_nth_data(list, n);
01082         iter->user_data3 = GINT_TO_POINTER(n);
01083         LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
01084         return iter->user_data2 != NULL;
01085     }
01086 
01087     if (parent->user_data == ITER_IS_COMMODITY)
01088     {
01089         commodity = (gnc_commodity *)parent->user_data2;
01090         list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
01091 
01092         iter->stamp      = model->stamp;
01093         iter->user_data  = ITER_IS_PRICE;
01094         iter->user_data2 = g_list_nth_data(list, n);
01095         iter->user_data3 = GINT_TO_POINTER(n);
01096         gnc_price_list_destroy(list);
01097         LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
01098         return iter->user_data2 != NULL;
01099     }
01100 
01101     iter->stamp = 0;
01102     LEAVE("FALSE");
01103     return FALSE;
01104 }
01105 
01106 static gboolean
01107 gnc_tree_model_price_iter_parent (GtkTreeModel *tree_model,
01108                                   GtkTreeIter *iter,
01109                                   GtkTreeIter *child)
01110 {
01111     GncTreeModelPrice *model;
01112     GncTreeModelPricePrivate *priv;
01113     gnc_commodity_table *ct;
01114     gnc_commodity * commodity;
01115     gnc_commodity_namespace *namespace;
01116     GList *list;
01117 
01118     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (tree_model), FALSE);
01119     g_return_val_if_fail (iter != NULL, FALSE);
01120     g_return_val_if_fail (child != NULL, FALSE);
01121 
01122     model = GNC_TREE_MODEL_PRICE (tree_model);
01123     ENTER("model %p, iter %p, child %p (%s)",
01124           tree_model, iter, child, iter_to_string(model, child));
01125 
01126     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
01127     if (child->user_data == ITER_IS_NAMESPACE)
01128     {
01129         LEAVE("ns has no parent");
01130         return FALSE;
01131     }
01132 
01133     if (child->user_data == ITER_IS_COMMODITY)
01134     {
01135         ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
01136         list = gnc_commodity_table_get_namespaces_list(ct);
01137         namespace = gnc_commodity_get_namespace_ds((gnc_commodity*)child->user_data2);
01138 
01139         iter->stamp      = model->stamp;
01140         iter->user_data  = ITER_IS_NAMESPACE;
01141         iter->user_data2 = namespace;
01142         iter->user_data3 = GINT_TO_POINTER(g_list_index(list, namespace));
01143         LEAVE("ns iter %p (%s)", iter, iter_to_string(model, iter));
01144         return TRUE;
01145     }
01146 
01147     commodity = gnc_price_get_commodity ((GNCPrice*)child->user_data2);
01148     namespace = gnc_commodity_get_namespace_ds(commodity);
01149     list = gnc_commodity_namespace_get_commodity_list(namespace);
01150 
01151     iter->stamp      = model->stamp;
01152     iter->user_data  = ITER_IS_COMMODITY;
01153     iter->user_data2 = commodity;
01154     iter->user_data3 = GINT_TO_POINTER(g_list_index(list, commodity));
01155     LEAVE("cm iter %p (%s)", iter, iter_to_string(model, iter));
01156     return TRUE;
01157 }
01158 
01159 /************************************************************/
01160 /*                Price Tree View Functions                 */
01161 /************************************************************/
01162 
01163 /*
01164  * Convert a model/price pair into a gtk_tree_model_iter.  This
01165  * routine should only be called from the file
01166  * gnc-tree-view-price.c.
01167  */
01168 gboolean
01169 gnc_tree_model_price_get_iter_from_price (GncTreeModelPrice *model,
01170         GNCPrice *price,
01171         GtkTreeIter *iter)
01172 {
01173     GncTreeModelPricePrivate *priv;
01174     gnc_commodity *commodity;
01175     GList *list;
01176     gint n;
01177 
01178     ENTER("model %p, price %p, iter %p", model, price, iter);
01179     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
01180     g_return_val_if_fail ((price != NULL), FALSE);
01181     g_return_val_if_fail ((iter != NULL), FALSE);
01182 
01183     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
01184     commodity = gnc_price_get_commodity(price);
01185     if (commodity == NULL)
01186     {
01187         LEAVE("no commodity");
01188         return FALSE;
01189     }
01190 
01191     list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
01192     if (list == NULL)
01193     {
01194         LEAVE("empty list");
01195         return FALSE;
01196     }
01197 
01198     n = g_list_index(list, price);
01199     if (n == -1)
01200     {
01201         gnc_price_list_destroy(list);
01202         LEAVE("not in list");
01203         return FALSE;
01204     }
01205 
01206     iter->stamp = model->stamp;
01207     iter->user_data  = ITER_IS_PRICE;
01208     iter->user_data2 = price;
01209     iter->user_data3 = GINT_TO_POINTER(n);
01210     gnc_price_list_destroy(list);
01211     LEAVE("iter %s", iter_to_string(model, iter));
01212     return TRUE;
01213 }
01214 
01215 /*
01216  * Convert a model/price pair into a gtk_tree_model_path.  This
01217  * routine should only be called from the file
01218  * gnc-tree-view-price.c.
01219  */
01220 GtkTreePath *
01221 gnc_tree_model_price_get_path_from_price (GncTreeModelPrice *model,
01222         GNCPrice *price)
01223 {
01224     GtkTreeIter tree_iter;
01225     GtkTreePath *tree_path;
01226 
01227     ENTER("model %p, price %p", model, price);
01228     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), NULL);
01229     g_return_val_if_fail (price != NULL, NULL);
01230 
01231     if (!gnc_tree_model_price_get_iter_from_price (model, price, &tree_iter))
01232     {
01233         LEAVE("no iter");
01234         return NULL;
01235     }
01236 
01237     tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
01238     if (tree_path)
01239     {
01240         gchar *path_string = gtk_tree_path_to_string(tree_path);
01241         LEAVE("path (2) %s", path_string);
01242         g_free(path_string);
01243     }
01244     else
01245     {
01246         LEAVE("no path");
01247     }
01248     return tree_path;
01249 }
01250 
01251 /*
01252  * Convert a model/commodity pair into a gtk_tree_model_iter.  This
01253  * routine should only be called from the file
01254  * gnc-tree-view-price.c.
01255  */
01256 gboolean
01257 gnc_tree_model_price_get_iter_from_commodity (GncTreeModelPrice *model,
01258         gnc_commodity *commodity,
01259         GtkTreeIter *iter)
01260 {
01261     gnc_commodity_namespace *namespace;
01262     GList *list;
01263     gint n;
01264 
01265     ENTER("model %p, commodity %p, iter %p", model, commodity, iter);
01266     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
01267     g_return_val_if_fail ((commodity != NULL), FALSE);
01268     g_return_val_if_fail ((iter != NULL), FALSE);
01269 
01270     namespace = gnc_commodity_get_namespace_ds(commodity);
01271     if (namespace == NULL)
01272     {
01273         LEAVE("no namespace");
01274         return FALSE;
01275     }
01276 
01277     list = gnc_commodity_namespace_get_commodity_list(namespace);
01278     if (list == NULL)
01279     {
01280         LEAVE("empty list");
01281         return FALSE;
01282     }
01283 
01284     n = g_list_index(list, commodity);
01285     if (n == -1)
01286     {
01287         LEAVE("not in list");
01288         return FALSE;
01289     }
01290 
01291     iter->stamp = model->stamp;
01292     iter->user_data  = ITER_IS_COMMODITY;
01293     iter->user_data2 = commodity;
01294     iter->user_data3 = GINT_TO_POINTER(n);
01295     LEAVE("iter %s", iter_to_string(model, iter));
01296     return TRUE;
01297 }
01298 
01299 /*
01300  * Convert a model/namespace pair into a gtk_tree_model_iter.  This
01301  * routine should only be called from the file
01302  * gnc-tree-view-price.c.
01303  */
01304 gboolean
01305 gnc_tree_model_price_get_iter_from_namespace (GncTreeModelPrice *model,
01306         gnc_commodity_namespace *namespace,
01307         GtkTreeIter *iter)
01308 {
01309     GncTreeModelPricePrivate *priv;
01310     gnc_commodity_table *ct;
01311     GList *list;
01312     gint n;
01313 
01314     ENTER("model %p, namespace %p, iter %p", model, namespace, iter);
01315     g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
01316     g_return_val_if_fail ((namespace != NULL), FALSE);
01317     g_return_val_if_fail ((iter != NULL), FALSE);
01318 
01319     priv = GNC_TREE_MODEL_PRICE_GET_PRIVATE(model);
01320     ct = qof_book_get_data (priv->book, GNC_COMMODITY_TABLE);
01321     list = gnc_commodity_table_get_namespaces_list(ct);
01322     if (list == NULL)
01323         return FALSE;
01324 
01325     n = g_list_index(list, namespace);
01326     if (n == -1)
01327         return FALSE;
01328 
01329     iter->stamp = model->stamp;
01330     iter->user_data  = ITER_IS_NAMESPACE;
01331     iter->user_data2 = namespace;
01332     iter->user_data3 = GINT_TO_POINTER(n);
01333     LEAVE("iter %s", iter_to_string(model, iter));
01334     return TRUE;
01335 }
01336 
01337 /************************************************************/
01338 /*    Price Tree Model - Engine Event Handling Functions    */
01339 /************************************************************/
01340 
01341 typedef struct _remove_data
01342 {
01343     GncTreeModelPrice *model;
01344     GtkTreePath       *path;
01345 } remove_data;
01346 
01347 static GSList *pending_removals = NULL;
01348 
01360 static void
01361 gnc_tree_model_price_row_add (GncTreeModelPrice *model,
01362                               GtkTreeIter *iter)
01363 {
01364     GtkTreePath *path;
01365     GtkTreeModel *tree_model;
01366     GtkTreeIter tmp_iter;
01367     gint i;
01368 
01369     ENTER("model %p, iter (%p)%s", model, iter, iter_to_string(model, iter));
01370 
01371     /* We're adding a row, so the lists on which this model is based have
01372      * changed. Since existing iterators (except the one just passed in)
01373      * are all based on old indexes into those lists, we need to invalidate
01374      * them, which we can do by changing the model's stamp. */
01375     do
01376     {
01377         model->stamp++;
01378     }
01379     while (model->stamp == 0);
01380     iter->stamp = model->stamp;
01381 
01382     /* Tag the new row as inserted. */
01383     tree_model = GTK_TREE_MODEL(model);
01384     path = gnc_tree_model_price_get_path (tree_model, iter);
01385     gtk_tree_model_row_inserted (tree_model, path, iter);
01386 
01387     /* Inform all ancestors. */
01388     /*
01389      * Charles Day: I don't think calls to gtk_tree_model_row_changed() should
01390      * be necessary. It is just a workaround for bug #540201.
01391      */
01392     if (gtk_tree_path_up(path) &&
01393             gtk_tree_path_get_depth(path) > 0 &&
01394             gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
01395     {
01396         /* Signal the change to the parent. */
01397         gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
01398 
01399         /* Is this the parent's first child? */
01400         if (gtk_tree_model_iter_n_children(tree_model, &tmp_iter) == 1)
01401             gtk_tree_model_row_has_child_toggled(tree_model, path, &tmp_iter);
01402 
01403         /* Signal any other ancestors. */
01404         while (gtk_tree_path_up(path) &&
01405                 gtk_tree_path_get_depth(path) > 0 &&
01406                 gtk_tree_model_get_iter(tree_model, &tmp_iter, path))
01407         {
01408             gtk_tree_model_row_changed(tree_model, path, &tmp_iter);
01409         }
01410     }
01411     gtk_tree_path_free(path);
01412 
01413     /* If the new row already has children, signal that so the expander
01414      * can be shown. This can happen, for example, if a namespace or
01415      * commodity is changed in another place (like the Security Editor)
01416      * and gets removed and then re-added to the commodity db. */
01417     if (gnc_tree_model_price_iter_has_child(tree_model, iter))
01418     {
01419         path = gnc_tree_model_price_get_path(tree_model, iter);
01420         gtk_tree_model_row_has_child_toggled(tree_model, path, iter);
01421         gtk_tree_path_free(path);
01422     }
01423 
01424     LEAVE(" ");
01425 }
01426 
01438 static void
01439 gnc_tree_model_price_row_delete (GncTreeModelPrice *model,
01440                                  GtkTreePath *path)
01441 {
01442     GtkTreeModel *tree_model;
01443     GtkTreeIter iter;
01444 
01445     g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
01446     g_return_if_fail(path);
01447 
01448     debug_path(ENTER, path);
01449 
01450     tree_model = GTK_TREE_MODEL(model);
01451 
01452     /* We're removing a row, so the lists on which this model is based have
01453      * changed. Since existing iterators are all based on old indexes into
01454      * those lists, we need to invalidate them, which we can do by changing
01455      * the model's stamp. */
01456     do
01457     {
01458         model->stamp++;
01459     }
01460     while (model->stamp == 0);
01461 
01462     /* Signal that the path has been deleted. */
01463     gtk_tree_model_row_deleted(tree_model, path);
01464 
01465     /* Issue any appropriate signals to ancestors. */
01466     if (gtk_tree_path_up(path) &&
01467             gtk_tree_path_get_depth(path) > 0 &&
01468             gtk_tree_model_get_iter(tree_model, &iter, path))
01469     {
01470         DEBUG("iter %s", iter_to_string(model, &iter));
01471 
01472         /* Signal the change to the parent. */
01473         gtk_tree_model_row_changed(tree_model, path, &iter);
01474 
01475         /* Was this the parent's only child? */
01476         if (!gtk_tree_model_iter_has_child(tree_model, &iter))
01477             gtk_tree_model_row_has_child_toggled(tree_model, path, &iter);
01478 
01479         /* Signal any other ancestors. */
01480         while (gtk_tree_path_up(path) &&
01481                 gtk_tree_path_get_depth(path) > 0 &&
01482                 gtk_tree_model_get_iter(tree_model, &iter, path))
01483         {
01484             DEBUG("iter %s", iter_to_string(model, &iter));
01485             gtk_tree_model_row_changed(tree_model, path, &iter);
01486         }
01487     }
01488 
01489     LEAVE(" ");
01490 }
01491 
01492 
01509 static gboolean
01510 gnc_tree_model_price_do_deletions (gpointer unused)
01511 {
01512     ENTER(" ");
01513 
01514     /* Go through the list of paths needing removal. */
01515     while (pending_removals)
01516     {
01517         remove_data *data = pending_removals->data;
01518         pending_removals = g_slist_delete_link(pending_removals, pending_removals);
01519 
01520         if (data)
01521         {
01522             debug_path(DEBUG, data->path);
01523 
01524             /* Remove the path. */
01525             gnc_tree_model_price_row_delete(data->model, data->path);
01526 
01527             gtk_tree_path_free(data->path);
01528             g_free(data);
01529         }
01530     }
01531 
01532     LEAVE(" ");
01533     /* Don't call me again. */
01534     return FALSE;
01535 }
01536 
01537 
01569 static void
01570 gnc_tree_model_price_event_handler (QofInstance *entity,
01571                                     QofEventId event_type,
01572                                     gpointer user_data,
01573                                     gpointer event_data)
01574 {
01575     GncTreeModelPrice *model;
01576     GtkTreePath *path;
01577     GtkTreeIter iter;
01578     remove_data *data;
01579     const gchar *name;
01580 
01581     ENTER("entity %p, event %d, model %p, event data %p",
01582           entity, event_type, user_data, event_data);
01583     model = (GncTreeModelPrice *)user_data;
01584 
01585     /* Do deletions if any are pending. */
01586     if (pending_removals)
01587         gnc_tree_model_price_do_deletions(NULL);
01588 
01589     /* hard failures */
01590     g_return_if_fail(GNC_IS_TREE_MODEL_PRICE(model));
01591 
01592     /* get type specific data */
01593     if (GNC_IS_COMMODITY(entity))
01594     {
01595         gnc_commodity *commodity;
01596 
01597         commodity = GNC_COMMODITY(entity);
01598         name = gnc_commodity_get_mnemonic(commodity);
01599         if (event_type != QOF_EVENT_DESTROY)
01600         {
01601             if (!gnc_tree_model_price_get_iter_from_commodity (model, commodity, &iter))
01602             {
01603                 LEAVE("no iter");
01604                 return;
01605             }
01606         }
01607     }
01608     else if (GNC_IS_COMMODITY_NAMESPACE(entity))
01609     {
01610         gnc_commodity_namespace *namespace;
01611 
01612         namespace = GNC_COMMODITY_NAMESPACE(entity);
01613         name = gnc_commodity_namespace_get_name(namespace);
01614         if (event_type != QOF_EVENT_DESTROY)
01615         {
01616             if (!gnc_tree_model_price_get_iter_from_namespace (model, namespace, &iter))
01617             {
01618                 LEAVE("no iter");
01619                 return;
01620             }
01621         }
01622     }
01623     else if (GNC_IS_PRICE(entity))
01624     {
01625         GNCPrice *price;
01626 
01627         price = GNC_PRICE(entity);
01628         name = "price";
01629         if (event_type != QOF_EVENT_DESTROY)
01630         {
01631             if (!gnc_tree_model_price_get_iter_from_price (model, price, &iter))
01632             {
01633                 LEAVE("no iter");
01634                 return;
01635             }
01636         }
01637     }
01638     else
01639     {
01640         return;
01641     }
01642 
01643     switch (event_type)
01644     {
01645     case QOF_EVENT_ADD:
01646         /* Tell the filters/views where the new account was added. */
01647         DEBUG("add %s", name);
01648         gnc_tree_model_price_row_add (model, &iter);
01649         break;
01650 
01651     case QOF_EVENT_REMOVE:
01652         /* Record the path of this account for later use in destruction */
01653         DEBUG("remove %s", name);
01654         path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
01655         if (path == NULL)
01656         {
01657             LEAVE("not in model");
01658             return;
01659         }
01660 
01661         data = g_new0 (remove_data, 1);
01662         data->model = model;
01663         data->path = path;
01664         pending_removals = g_slist_append (pending_removals, data);
01665         g_idle_add_full(G_PRIORITY_HIGH_IDLE,
01666                         gnc_tree_model_price_do_deletions, NULL, NULL);
01667 
01668         LEAVE(" ");
01669         return;
01670 
01671     case QOF_EVENT_MODIFY:
01672         DEBUG("change %s", name);
01673         path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
01674         if (path == NULL)
01675         {
01676             LEAVE("not in model");
01677             return;
01678         }
01679         if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &iter, path))
01680         {
01681             gtk_tree_path_free(path);
01682             LEAVE("can't find iter for path");
01683             return;
01684         }
01685         gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter);
01686         gtk_tree_path_free(path);
01687         LEAVE(" ");
01688         return;
01689 
01690     default:
01691         LEAVE("ignored event for %s", name);
01692         return;
01693     }
01694     LEAVE(" new stamp %u", model->stamp);
01695 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines