GnuCash 2.4.99
gnc-tree-view-price.c
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 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines