|
GnuCash 2.4.99
|
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 (>mits_buffer_key); 00386 if (string == NULL) 00387 { 00388 string = g_malloc(ITER_STRING_LEN + 1); 00389 g_static_private_set (>mits_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 }
1.7.4