|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * gnc-tree-view-price.c -- GtkTreeView implementation to display * 00003 * prices in a GtkTreeView. * 00004 * Copyright (C) 2003,2005 David Hampton <hampton@employees.org> * 00005 * * 00006 * This program is free software; you can redistribute it and/or * 00007 * modify it under the terms of the GNU General Public License as * 00008 * published by the Free Software Foundation; either version 2 of * 00009 * the License, or (at your option) any later version. * 00010 * * 00011 * This program is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00014 * GNU General Public License for more details. * 00015 * * 00016 * You should have received a copy of the GNU General Public License* 00017 * along with this program; if not, contact: * 00018 * * 00019 * Free Software Foundation Voice: +1-617-542-5942 * 00020 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00021 * Boston, MA 02110-1301, USA gnu@gnu.org * 00022 * * 00023 \********************************************************************/ 00024 00025 #include "config.h" 00026 00027 #include <gtk/gtk.h> 00028 #include <glib/gi18n.h> 00029 #include <string.h> 00030 00031 #include "gnc-tree-view.h" 00032 #include "gnc-tree-model-price.h" 00033 #include "gnc-tree-view-price.h" 00034 00035 #include "gnc-pricedb.h" 00036 #include "gnc-component-manager.h" 00037 #include "gnc-engine.h" 00038 #include "gnc-gconf-utils.h" 00039 #include "gnc-glib-utils.h" 00040 #include "gnc-gnome-utils.h" 00041 #include "gnc-icons.h" 00042 #include "gnc-ui-util.h" 00043 00044 00047 /* This static indicates the debugging module that this .o belongs to. */ 00048 static QofLogModule log_module = GNC_MOD_GUI; 00049 00051 static void gnc_tree_view_price_class_init (GncTreeViewPriceClass *klass); 00052 static void gnc_tree_view_price_init (GncTreeViewPrice *view); 00053 static void gnc_tree_view_price_finalize (GObject *object); 00054 static void gnc_tree_view_price_destroy (GtkObject *object); 00055 00056 typedef struct GncTreeViewPricePrivate 00057 { 00058 gpointer dummy; 00059 } GncTreeViewPricePrivate; 00060 00061 #define GNC_TREE_VIEW_PRICE_GET_PRIVATE(o) \ 00062 (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_TREE_VIEW_PRICE, GncTreeViewPricePrivate)) 00063 00064 00065 /************************************************************/ 00066 /* g_object required functions */ 00067 /************************************************************/ 00068 00069 static GObjectClass *parent_class = NULL; 00070 00071 GType 00072 gnc_tree_view_price_get_type (void) 00073 { 00074 static GType gnc_tree_view_price_type = 0; 00075 00076 if (gnc_tree_view_price_type == 0) 00077 { 00078 static const GTypeInfo our_info = 00079 { 00080 sizeof (GncTreeViewPriceClass), 00081 NULL, 00082 NULL, 00083 (GClassInitFunc) gnc_tree_view_price_class_init, 00084 NULL, 00085 NULL, 00086 sizeof (GncTreeViewPrice), 00087 0, 00088 (GInstanceInitFunc) gnc_tree_view_price_init 00089 }; 00090 00091 gnc_tree_view_price_type = g_type_register_static (GNC_TYPE_TREE_VIEW, 00092 "GncTreeViewPrice", 00093 &our_info, 0); 00094 } 00095 00096 return gnc_tree_view_price_type; 00097 } 00098 00099 static void 00100 gnc_tree_view_price_class_init (GncTreeViewPriceClass *klass) 00101 { 00102 GObjectClass *o_class; 00103 GtkObjectClass *object_class; 00104 00105 parent_class = g_type_class_peek_parent (klass); 00106 00107 o_class = G_OBJECT_CLASS (klass); 00108 object_class = GTK_OBJECT_CLASS (klass); 00109 00110 /* GObject signals */ 00111 o_class->finalize = gnc_tree_view_price_finalize; 00112 00113 /* GtkObject signals */ 00114 object_class->destroy = gnc_tree_view_price_destroy; 00115 00116 g_type_class_add_private(klass, sizeof(GncTreeViewPricePrivate)); 00117 } 00118 00119 static void 00120 gnc_tree_view_price_init (GncTreeViewPrice *view) 00121 { 00122 } 00123 00124 static void 00125 gnc_tree_view_price_finalize (GObject *object) 00126 { 00127 GncTreeViewPrice *view; 00128 GncTreeViewPricePrivate *priv; 00129 00130 ENTER("view %p", object); 00131 gnc_leave_return_if_fail (object != NULL); 00132 gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_PRICE (object)); 00133 00134 view = GNC_TREE_VIEW_PRICE (object); 00135 priv = GNC_TREE_VIEW_PRICE_GET_PRIVATE (view); 00136 00137 if (G_OBJECT_CLASS (parent_class)->finalize) 00138 (* G_OBJECT_CLASS (parent_class)->finalize) (object); 00139 LEAVE(" "); 00140 } 00141 00142 static void 00143 gnc_tree_view_price_destroy (GtkObject *object) 00144 { 00145 GncTreeViewPrice *view; 00146 00147 ENTER("view %p", object); 00148 gnc_leave_return_if_fail (object != NULL); 00149 gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_PRICE (object)); 00150 00151 view = GNC_TREE_VIEW_PRICE (object); 00152 00153 if (GTK_OBJECT_CLASS (parent_class)->destroy) 00154 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); 00155 LEAVE(" "); 00156 } 00157 00158 00159 /************************************************************/ 00160 /* sort functions */ 00161 /************************************************************/ 00162 00163 static gboolean 00164 get_prices (GtkTreeModel *f_model, 00165 GtkTreeIter *f_iter_a, 00166 GtkTreeIter *f_iter_b, 00167 GNCPrice **price_a, 00168 GNCPrice **price_b) 00169 { 00170 GncTreeModelPrice *model; 00171 GtkTreeModel *tree_model; 00172 GtkTreeIter iter_a, iter_b; 00173 00174 tree_model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); 00175 model = GNC_TREE_MODEL_PRICE(tree_model); 00176 00177 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model), 00178 &iter_a, 00179 f_iter_a); 00180 00181 /* The iters must point to prices for this to be meaningful */ 00182 if (!gnc_tree_model_price_iter_is_price (model, &iter_a)) 00183 return FALSE; 00184 00185 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model), 00186 &iter_b, 00187 f_iter_b); 00188 00189 *price_a = gnc_tree_model_price_get_price (model, &iter_a); 00190 *price_b = gnc_tree_model_price_get_price (model, &iter_b); 00191 return TRUE; 00192 } 00193 00194 static gint 00195 sort_ns_or_cm (GtkTreeModel *f_model, 00196 GtkTreeIter *f_iter_a, 00197 GtkTreeIter *f_iter_b) 00198 { 00199 GncTreeModelPrice *model; 00200 GtkTreeModel *tree_model; 00201 GtkTreeIter iter_a, iter_b; 00202 gnc_commodity_namespace *ns_a, *ns_b; 00203 gnc_commodity *comm_a, *comm_b; 00204 00205 tree_model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); 00206 model = GNC_TREE_MODEL_PRICE(tree_model); 00207 00208 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model), 00209 &iter_a, 00210 f_iter_a); 00211 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model), 00212 &iter_b, 00213 f_iter_b); 00214 00215 if (gnc_tree_model_price_iter_is_namespace (model, &iter_a)) 00216 { 00217 ns_a = gnc_tree_model_price_get_namespace (model, &iter_a); 00218 ns_b = gnc_tree_model_price_get_namespace (model, &iter_b); 00219 return safe_utf8_collate (gnc_commodity_namespace_get_name (ns_a), 00220 gnc_commodity_namespace_get_name (ns_b)); 00221 } 00222 00223 comm_a = gnc_tree_model_price_get_commodity (model, &iter_a); 00224 comm_b = gnc_tree_model_price_get_commodity (model, &iter_b); 00225 return safe_utf8_collate (gnc_commodity_get_mnemonic (comm_a), 00226 gnc_commodity_get_mnemonic (comm_b)); 00227 } 00228 00229 static gint 00230 default_sort (GNCPrice *price_a, GNCPrice *price_b) 00231 { 00232 gnc_commodity *curr_a, *curr_b; 00233 Timespec ts_a, ts_b; 00234 gint result; 00235 00236 /* Primary sort (i.e. commodity name) handled by the tree structure. */ 00237 00238 /* secondary sort: currency */ 00239 curr_a = gnc_price_get_currency (price_a); 00240 curr_b = gnc_price_get_currency (price_b); 00241 00242 result = safe_utf8_collate (gnc_commodity_get_namespace (curr_a), 00243 gnc_commodity_get_namespace (curr_b)); 00244 if (result != 0) return result; 00245 00246 result = safe_utf8_collate (gnc_commodity_get_mnemonic (curr_a), 00247 gnc_commodity_get_mnemonic (curr_b)); 00248 if (result != 0) return result; 00249 00250 /* tertiary sort: time */ 00251 ts_a = gnc_price_get_time (price_a); 00252 ts_b = gnc_price_get_time (price_b); 00253 result = timespec_cmp (&ts_a, &ts_b); 00254 if (result) 00255 /* Reverse the result to present the most recent quote first. */ 00256 return -result; 00257 00258 /* last sort: value */ 00259 return gnc_numeric_compare (gnc_price_get_value (price_a), 00260 gnc_price_get_value (price_b)); 00261 } 00262 00263 static gint 00264 sort_by_name (GtkTreeModel *f_model, 00265 GtkTreeIter *f_iter_a, 00266 GtkTreeIter *f_iter_b, 00267 gpointer user_data) 00268 { 00269 GNCPrice *price_a, *price_b; 00270 00271 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b)) 00272 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b); 00273 00274 return default_sort (price_a, price_b); 00275 } 00276 00277 static gint 00278 sort_by_date (GtkTreeModel *f_model, 00279 GtkTreeIter *f_iter_a, 00280 GtkTreeIter *f_iter_b, 00281 gpointer user_data) 00282 { 00283 GNCPrice *price_a, *price_b; 00284 Timespec ts_a, ts_b; 00285 gboolean result; 00286 00287 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b)) 00288 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b); 00289 00290 /* sort by time first */ 00291 ts_a = gnc_price_get_time (price_a); 00292 ts_b = gnc_price_get_time (price_b); 00293 result = timespec_cmp (&ts_a, &ts_b); 00294 if (result) 00295 /* Reverse the result to present the most recent quote first. */ 00296 return -result; 00297 00298 return default_sort (price_a, price_b); 00299 } 00300 00301 static gint 00302 sort_by_source (GtkTreeModel *f_model, 00303 GtkTreeIter *f_iter_a, 00304 GtkTreeIter *f_iter_b, 00305 gpointer user_data) 00306 { 00307 GNCPrice *price_a, *price_b; 00308 gint result; 00309 00310 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b)) 00311 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b); 00312 00313 /* sort by source first */ 00314 result = safe_utf8_collate (gnc_price_get_source (price_a), 00315 gnc_price_get_source (price_b)); 00316 if (result != 0) 00317 return result; 00318 00319 return default_sort (price_a, price_b); 00320 } 00321 00322 static gint 00323 sort_by_type (GtkTreeModel *f_model, 00324 GtkTreeIter *f_iter_a, 00325 GtkTreeIter *f_iter_b, 00326 gpointer user_data) 00327 { 00328 GNCPrice *price_a, *price_b; 00329 gint result; 00330 00331 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b)) 00332 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b); 00333 00334 /* sort by source first */ 00335 result = safe_utf8_collate (gnc_price_get_typestr (price_a), 00336 gnc_price_get_typestr (price_b)); 00337 if (result != 0) 00338 return result; 00339 00340 return default_sort (price_a, price_b); 00341 } 00342 00343 static gint 00344 sort_by_value (GtkTreeModel *f_model, 00345 GtkTreeIter *f_iter_a, 00346 GtkTreeIter *f_iter_b, 00347 gpointer user_data) 00348 { 00349 gnc_commodity *comm_a, *comm_b; 00350 GNCPrice *price_a, *price_b; 00351 gboolean result; 00352 gint value; 00353 00354 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b)) 00355 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b); 00356 00357 /* 00358 * Sorted by commodity because of the tree structure. Now sort by 00359 * currency so we're only comparing numbers in the same currency 00360 * denomination. 00361 */ 00362 comm_a = gnc_price_get_currency (price_a); 00363 comm_b = gnc_price_get_currency (price_b); 00364 if (comm_a && comm_b) 00365 { 00366 value = safe_utf8_collate (gnc_commodity_get_namespace (comm_a), 00367 gnc_commodity_get_namespace (comm_b)); 00368 if (value != 0) 00369 return value; 00370 value = safe_utf8_collate (gnc_commodity_get_mnemonic (comm_a), 00371 gnc_commodity_get_mnemonic (comm_b)); 00372 if (value != 0) 00373 return value; 00374 } 00375 00376 /* 00377 * Now do the actual price comparison now we're sure that its an 00378 * apples to apples comparison. 00379 */ 00380 result = gnc_numeric_compare (gnc_price_get_value (price_a), 00381 gnc_price_get_value (price_b)); 00382 if (result) 00383 return result; 00384 00385 return default_sort (price_a, price_b); 00386 } 00387 00388 00389 /************************************************************/ 00390 /* New View Creation */ 00391 /************************************************************/ 00392 00393 /* 00394 * Create a new price tree view with (optional) top level root node. 00395 * This view will be based on a model that is common to all view of 00396 * the same set of books, but will have its own private filter on that 00397 * model. 00398 */ 00399 GtkTreeView * 00400 gnc_tree_view_price_new (QofBook *book, 00401 const gchar *first_property_name, 00402 ...) 00403 { 00404 GncTreeView *view; 00405 GtkTreeModel *model, *f_model, *s_model; 00406 GtkTreeViewColumn *col; 00407 GNCPriceDB *price_db; 00408 va_list var_args; 00409 const gchar *sample_text; 00410 gchar *sample_text2; 00411 00412 ENTER(" "); 00413 /* Create/get a pointer to the existing model for this set of books. */ 00414 price_db = gnc_pricedb_get_db(book); 00415 model = gnc_tree_model_price_new (book, price_db); 00416 00417 /* Set up the view private filter on the common model. */ 00418 f_model = gtk_tree_model_filter_new (model, NULL); 00419 g_object_unref(G_OBJECT(model)); 00420 s_model = gtk_tree_model_sort_new_with_model (f_model); 00421 g_object_unref(G_OBJECT(f_model)); 00422 00423 /* Create our view */ 00424 view = g_object_new (GNC_TYPE_TREE_VIEW_PRICE, 00425 "name", "price_tree", NULL); 00426 gnc_tree_view_set_model (view, s_model); 00427 g_object_unref(G_OBJECT(s_model)); 00428 00429 DEBUG("model ref count is %d", G_OBJECT(model)->ref_count); 00430 DEBUG("f_model ref count is %d", G_OBJECT(f_model)->ref_count); 00431 DEBUG("s_model ref count is %d", G_OBJECT(s_model)->ref_count); 00432 00433 sample_text = gnc_commodity_get_printname(gnc_default_currency()); 00434 sample_text2 = g_strdup_printf("%s%s", sample_text, sample_text); 00435 col = gnc_tree_view_add_text_column ( 00436 view, _("Security"), "security", NULL, sample_text2, 00437 GNC_TREE_MODEL_PRICE_COL_COMMODITY, 00438 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, 00439 sort_by_name); 00440 g_free(sample_text2); 00441 col = gnc_tree_view_add_text_column ( 00442 view, _("Currency"), "currency", NULL, sample_text, 00443 GNC_TREE_MODEL_PRICE_COL_CURRENCY, 00444 GNC_TREE_MODEL_PRICE_COL_VISIBILITY, 00445 sort_by_name); 00446 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); 00447 col = gnc_tree_view_add_text_column ( 00448 view, _("Date"), "date", NULL, "2005-05-20", 00449 GNC_TREE_MODEL_PRICE_COL_DATE, 00450 GNC_TREE_MODEL_PRICE_COL_VISIBILITY, 00451 sort_by_date); 00452 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); 00453 col = gnc_tree_view_add_text_column ( 00454 view, _("Source"), "source", NULL, "Finance::Quote", 00455 GNC_TREE_MODEL_PRICE_COL_SOURCE, 00456 GNC_TREE_MODEL_PRICE_COL_VISIBILITY, 00457 sort_by_source); 00458 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); 00459 col = gnc_tree_view_add_text_column ( 00460 view, _("Type"), "type", NULL, "last", 00461 GNC_TREE_MODEL_PRICE_COL_TYPE, 00462 GNC_TREE_MODEL_PRICE_COL_VISIBILITY, 00463 sort_by_type); 00464 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); 00465 col = gnc_tree_view_add_numeric_column ( 00466 view, _("Price"), "price", "100.00000", 00467 GNC_TREE_MODEL_PRICE_COL_VALUE, 00468 GNC_TREE_VIEW_COLUMN_COLOR_NONE, 00469 GNC_TREE_MODEL_PRICE_COL_VISIBILITY, 00470 sort_by_value); 00471 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); 00472 00473 gnc_tree_view_configure_columns(view); 00474 00475 /* Set properties */ 00476 va_start (var_args, first_property_name); 00477 g_object_set_valist (G_OBJECT(view), first_property_name, var_args); 00478 va_end (var_args); 00479 00480 /* Sort on the commodity column by default. This allows for a consistent 00481 * sort if commodities are removed and re-added from the model. */ 00482 if (!gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(s_model), 00483 NULL, NULL)) 00484 { 00485 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model), 00486 GNC_TREE_MODEL_PRICE_COL_COMMODITY, 00487 GTK_SORT_ASCENDING); 00488 } 00489 00490 gtk_widget_show(GTK_WIDGET(view)); 00491 LEAVE(" %p", view); 00492 return GTK_TREE_VIEW(view); 00493 } 00494 00495 /************************************************************/ 00496 /* Auxiliary Functions */ 00497 /************************************************************/ 00498 00499 #define debug_path(fn, path) { \ 00500 gchar *path_string = gtk_tree_path_to_string(path); \ 00501 fn("tree path %s", path_string); \ 00502 g_free(path_string); \ 00503 } 00504 00505 static gboolean 00506 gnc_tree_view_price_get_iter_from_price (GncTreeViewPrice *view, 00507 GNCPrice *price, 00508 GtkTreeIter *s_iter) 00509 { 00510 GtkTreeModel *model, *f_model, *s_model; 00511 GtkTreeIter iter, f_iter; 00512 00513 g_return_val_if_fail(GNC_IS_TREE_VIEW_PRICE(view), FALSE); 00514 g_return_val_if_fail(price != NULL, FALSE); 00515 g_return_val_if_fail(s_iter != NULL, FALSE); 00516 00517 ENTER("view %p, price %p", view, price); 00518 00519 /* Reach down to the real model and get an iter for this price */ 00520 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); 00521 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); 00522 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); 00523 if (!gnc_tree_model_price_get_iter_from_price (GNC_TREE_MODEL_PRICE(model), price, &iter)) 00524 { 00525 LEAVE("model_get_iter_from_price failed"); 00526 return FALSE; 00527 } 00528 00529 /* convert back to a sort iter */ 00530 gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER(f_model), 00531 &f_iter, &iter); 00532 gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model), 00533 s_iter, &f_iter); 00534 LEAVE(" "); 00535 return TRUE; 00536 } 00537 00538 /************************************************************/ 00539 /* Price Tree View Filter Functions */ 00540 /************************************************************/ 00541 00542 /************************************************************/ 00543 /* Price Tree View Visibility Filter */ 00544 /************************************************************/ 00545 00546 typedef struct 00547 { 00548 gnc_tree_view_price_ns_filter_func user_ns_fn; 00549 gnc_tree_view_price_cm_filter_func user_cm_fn; 00550 gnc_tree_view_price_pc_filter_func user_pc_fn; 00551 gpointer user_data; 00552 GtkDestroyNotify user_destroy; 00553 } filter_user_data; 00554 00555 static void 00556 gnc_tree_view_price_filter_destroy (gpointer data) 00557 { 00558 filter_user_data *fd = data; 00559 00560 if (fd->user_destroy) 00561 fd->user_destroy(fd->user_data); 00562 g_free(fd); 00563 } 00564 00565 static gboolean 00566 gnc_tree_view_price_filter_helper (GtkTreeModel *model, 00567 GtkTreeIter *iter, 00568 gpointer data) 00569 { 00570 gnc_commodity_namespace *namespace; 00571 gnc_commodity *commodity; 00572 GNCPrice *price; 00573 filter_user_data *fd = data; 00574 00575 g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE); 00576 g_return_val_if_fail (iter != NULL, FALSE); 00577 00578 if (gnc_tree_model_price_iter_is_namespace (GNC_TREE_MODEL_PRICE(model), iter)) 00579 { 00580 if (fd->user_ns_fn) 00581 { 00582 namespace = gnc_tree_model_price_get_namespace (GNC_TREE_MODEL_PRICE(model), iter); 00583 return fd->user_ns_fn(namespace, fd->user_data); 00584 } 00585 return TRUE; 00586 } 00587 00588 if (gnc_tree_model_price_iter_is_commodity (GNC_TREE_MODEL_PRICE(model), iter)) 00589 { 00590 if (fd->user_cm_fn) 00591 { 00592 commodity = gnc_tree_model_price_get_commodity (GNC_TREE_MODEL_PRICE(model), iter); 00593 return fd->user_cm_fn(commodity, fd->user_data); 00594 } 00595 return TRUE; 00596 } 00597 00598 if (gnc_tree_model_price_iter_is_price (GNC_TREE_MODEL_PRICE(model), iter)) 00599 { 00600 if (fd->user_pc_fn) 00601 { 00602 price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model), iter); 00603 return fd->user_pc_fn(price, fd->user_data); 00604 } 00605 return TRUE; 00606 } 00607 00608 return FALSE; 00609 } 00610 00611 /* 00612 * Set an GtkTreeModel visible filter on this price. This filter will be 00613 * called for each price that the tree is about to show, and the 00614 * price will be passed to the callback function. 00615 */ 00616 void 00617 gnc_tree_view_price_set_filter (GncTreeViewPrice *view, 00618 gnc_tree_view_price_ns_filter_func ns_func, 00619 gnc_tree_view_price_cm_filter_func cm_func, 00620 gnc_tree_view_price_pc_filter_func pc_func, 00621 gpointer data, 00622 GtkDestroyNotify destroy) 00623 { 00624 GtkTreeModel *f_model, *s_model; 00625 filter_user_data *fd = data; 00626 00627 ENTER("view %p, ns func %p, cm func %p, pc func %p, data %p, destroy %p", 00628 view, ns_func, cm_func, pc_func, data, destroy); 00629 00630 g_return_if_fail(GNC_IS_TREE_VIEW_PRICE(view)); 00631 g_return_if_fail((ns_func != NULL) || (cm_func != NULL)); 00632 00633 fd = g_malloc(sizeof(filter_user_data)); 00634 fd->user_ns_fn = ns_func; 00635 fd->user_cm_fn = cm_func; 00636 fd->user_pc_fn = pc_func; 00637 fd->user_data = data; 00638 fd->user_destroy = destroy; 00639 00640 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); 00641 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); 00642 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model), 00643 gnc_tree_view_price_filter_helper, 00644 fd, 00645 gnc_tree_view_price_filter_destroy); 00646 00647 /* Whack any existing levels. The top two levels have been created 00648 * before this routine can be called. Unfortunately, if the just 00649 * applied filter filters out all the nodes in the tree, the gtk 00650 * code throws a critical error. This occurs when there are no 00651 * prices in the price database. Once the very first price has been 00652 * added this error message goes away. */ 00653 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model)); 00654 LEAVE(" "); 00655 } 00656 00657 /************************************************************/ 00658 /* Price Tree View Get/Set Functions */ 00659 /************************************************************/ 00660 00661 /* 00662 * Retrieve the selected price from an price tree view. The 00663 * price tree must be in single selection mode. 00664 */ 00665 GNCPrice * 00666 gnc_tree_view_price_get_selected_price (GncTreeViewPrice *view) 00667 { 00668 GtkTreeSelection *selection; 00669 GtkTreeModel *model, *f_model, *s_model; 00670 GtkTreeIter iter, f_iter, s_iter; 00671 GNCPrice *price; 00672 00673 ENTER("view %p", view); 00674 g_return_val_if_fail (GNC_IS_TREE_VIEW_PRICE (view), NULL); 00675 00676 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); 00677 if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter)) 00678 { 00679 LEAVE("no price, get_selected failed"); 00680 return FALSE; 00681 } 00682 00683 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), 00684 &f_iter, &s_iter); 00685 00686 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); 00687 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model), 00688 &iter, &f_iter); 00689 00690 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); 00691 price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model), 00692 &iter); 00693 LEAVE("price %p", price); 00694 return price; 00695 } 00696 00697 /* 00698 * Selects a single price in the price tree view. The price 00699 * tree must be in single selection mode. 00700 */ 00701 void 00702 gnc_tree_view_price_set_selected_price (GncTreeViewPrice *view, 00703 GNCPrice *price) 00704 { 00705 GtkTreeModel *model, *f_model, *s_model; 00706 GtkTreePath *path, *f_path, *s_path, *parent_path; 00707 GtkTreeSelection *selection; 00708 00709 ENTER("view %p, price %p", view, price); 00710 00711 /* Clear any existing selection. */ 00712 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); 00713 gtk_tree_selection_unselect_all (selection); 00714 00715 if (price == NULL) 00716 return; 00717 00718 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); 00719 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); 00720 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model)); 00721 00722 path = gnc_tree_model_price_get_path_from_price (GNC_TREE_MODEL_PRICE(model), price); 00723 if (path == NULL) 00724 { 00725 LEAVE("get_path_from_price failed"); 00726 return; 00727 } 00728 debug_path(DEBUG, path); 00729 00730 f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model), 00731 path); 00732 gtk_tree_path_free(path); 00733 if (f_path == NULL) 00734 { 00735 LEAVE("no filter path"); 00736 return; 00737 } 00738 debug_path(DEBUG, f_path); 00739 00740 s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model), 00741 f_path); 00742 gtk_tree_path_free(f_path); 00743 if (s_path == NULL) 00744 { 00745 LEAVE("no sort path"); 00746 return; 00747 } 00748 00749 /* gtk_tree_view requires that a row be visible before it can be selected */ 00750 parent_path = gtk_tree_path_copy (s_path); 00751 if (gtk_tree_path_up (parent_path)) 00752 { 00753 /* This function is misnamed. It expands the actual item 00754 * specified, not the path to the item specified. I.E. It expands 00755 * one level too many, thus the get of the parent. */ 00756 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path); 00757 } 00758 gtk_tree_path_free(parent_path); 00759 00760 gtk_tree_selection_select_path (selection, s_path); 00761 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0); 00762 debug_path(LEAVE, s_path); 00763 gtk_tree_path_free(s_path); 00764 } 00765 00766 /* 00767 * This helper function is called once for each row in the tree view 00768 * that is currently selected. Its task is to add the corresponding 00769 * price to the end of a glist. 00770 */ 00771 static void 00772 get_selected_prices_helper (GtkTreeModel *s_model, 00773 GtkTreePath *s_path, 00774 GtkTreeIter *s_iter, 00775 gpointer data) 00776 { 00777 GList **return_list = data; 00778 GtkTreeModel *model, *f_model; 00779 GtkTreeIter iter, f_iter; 00780 GNCPrice *price; 00781 00782 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), 00783 &f_iter, s_iter); 00784 00785 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); 00786 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model), 00787 &iter, &f_iter); 00788 00789 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); 00790 price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model), 00791 &iter); 00792 if (price) 00793 *return_list = g_list_append(*return_list, price); 00794 } 00795 00796 /* 00797 * Given an price tree view, return a list of the selected commodities. The 00798 * price tree must be in multiple selection mode. 00799 * 00800 * Note: It is the responsibility of the caller to free the returned 00801 * list. 00802 */ 00803 GList * 00804 gnc_tree_view_price_get_selected_prices (GncTreeViewPrice *view) 00805 { 00806 GtkTreeSelection *selection; 00807 GList *return_list = NULL; 00808 00809 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); 00810 gtk_tree_selection_selected_foreach(selection, get_selected_prices_helper, &return_list); 00811 return return_list; 00812 }
1.7.4