1 /********************************************************************\
2 * gnc-tree-view-price.c -- GtkTreeView implementation to display *
3 * prices in a GtkTreeView. *
4 * Copyright (C) 2003,2005 David Hampton <hampton@employees.org> *
5 * *
6 * This program is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU General Public License as *
8 * published by the Free Software Foundation; either version 2 of *
9 * the License, or (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License*
17 * along with this program; if not, contact: *
18 * *
19 * Free Software Foundation Voice: +1-617-542-5942 *
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 * Boston, MA 02110-1301, USA gnu@gnu.org *
22 * *
23 \********************************************************************/
24
25 #include <config.h>
26
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <string.h>
30
31 #include "gnc-tree-view.h"
32 #include "gnc-tree-model-price.h"
33 #include "gnc-tree-view-price.h"
34
35 #include "gnc-pricedb.h"
36 #include "gnc-component-manager.h"
37 #include "gnc-engine.h"
38 #include "gnc-glib-utils.h"
39 #include "gnc-gnome-utils.h"
40 #include "gnc-icons.h"
41 #include "gnc-ui-util.h"
42
43
44 /** Static Globals *******************************************************/
45
46 /* This static indicates the debugging module that this .o belongs to. */
47 static QofLogModule log_module = GNC_MOD_GUI;
48
49 /** Declarations *********************************************************/
50 static void gnc_tree_view_price_class_init (GncTreeViewPriceClass *klass);
51 static void gnc_tree_view_price_init (GncTreeViewPrice *view);
52 static void gnc_tree_view_price_finalize (GObject *object);
53 static void gnc_tree_view_price_destroy (GtkWidget *widget);
54
55 typedef struct GncTreeViewPricePrivate
56 {
57 gpointer dummy;
58 } GncTreeViewPricePrivate;
59
60 #define GNC_TREE_VIEW_PRICE_GET_PRIVATE(o) \
61 ((GncTreeViewPricePrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_TREE_VIEW_PRICE))
62
63
64 /************************************************************/
65 /* g_object required functions */
66 /************************************************************/
67
68 static GObjectClass *parent_class = NULL;
69
G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewPrice,gnc_tree_view_price,GNC_TYPE_TREE_VIEW)70 G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewPrice, gnc_tree_view_price, GNC_TYPE_TREE_VIEW)
71
72 static void
73 gnc_tree_view_price_class_init (GncTreeViewPriceClass *klass)
74 {
75 GObjectClass *o_class;
76 GtkWidgetClass *widget_class;
77
78 parent_class = g_type_class_peek_parent (klass);
79
80 o_class = G_OBJECT_CLASS (klass);
81 widget_class = GTK_WIDGET_CLASS (klass);
82
83 /* GObject signals */
84 o_class->finalize = gnc_tree_view_price_finalize;
85
86 /* GtkWidget signals */
87 widget_class->destroy = gnc_tree_view_price_destroy;
88 }
89
90 static void
gnc_tree_view_price_init(GncTreeViewPrice * view)91 gnc_tree_view_price_init (GncTreeViewPrice *view)
92 {
93 }
94
95 static void
gnc_tree_view_price_finalize(GObject * object)96 gnc_tree_view_price_finalize (GObject *object)
97 {
98 ENTER("view %p", object);
99 gnc_leave_return_if_fail (object != NULL);
100 gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_PRICE (object));
101
102 if (G_OBJECT_CLASS (parent_class)->finalize)
103 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
104 LEAVE(" ");
105 }
106
107 static void
gnc_tree_view_price_destroy(GtkWidget * widget)108 gnc_tree_view_price_destroy (GtkWidget *widget)
109 {
110 ENTER("view %p", widget);
111 gnc_leave_return_if_fail (widget != NULL);
112 gnc_leave_return_if_fail (GNC_IS_TREE_VIEW_PRICE (widget));
113
114 if (GTK_WIDGET_CLASS (parent_class)->destroy)
115 (* GTK_WIDGET_CLASS (parent_class)->destroy) (widget);
116 LEAVE(" ");
117 }
118
119
120 /************************************************************/
121 /* sort functions */
122 /************************************************************/
123
124 static gboolean
get_prices(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,GNCPrice ** price_a,GNCPrice ** price_b)125 get_prices (GtkTreeModel *f_model,
126 GtkTreeIter *f_iter_a,
127 GtkTreeIter *f_iter_b,
128 GNCPrice **price_a,
129 GNCPrice **price_b)
130 {
131 GncTreeModelPrice *model;
132 GtkTreeModel *tree_model;
133 GtkTreeIter iter_a, iter_b;
134
135 tree_model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
136 model = GNC_TREE_MODEL_PRICE(tree_model);
137
138 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
139 &iter_a,
140 f_iter_a);
141
142 /* The iters must point to prices for this to be meaningful */
143 if (!gnc_tree_model_price_iter_is_price (model, &iter_a))
144 return FALSE;
145
146 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
147 &iter_b,
148 f_iter_b);
149
150 *price_a = gnc_tree_model_price_get_price (model, &iter_a);
151 *price_b = gnc_tree_model_price_get_price (model, &iter_b);
152 return TRUE;
153 }
154
155 static gint
sort_ns_or_cm(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b)156 sort_ns_or_cm (GtkTreeModel *f_model,
157 GtkTreeIter *f_iter_a,
158 GtkTreeIter *f_iter_b)
159 {
160 GncTreeModelPrice *model;
161 GtkTreeModel *tree_model;
162 GtkTreeIter iter_a, iter_b;
163 gnc_commodity_namespace *ns_a, *ns_b;
164 gnc_commodity *comm_a, *comm_b;
165
166 tree_model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
167 model = GNC_TREE_MODEL_PRICE(tree_model);
168
169 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
170 &iter_a,
171 f_iter_a);
172 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
173 &iter_b,
174 f_iter_b);
175
176 if (gnc_tree_model_price_iter_is_namespace (model, &iter_a))
177 {
178 ns_a = gnc_tree_model_price_get_namespace (model, &iter_a);
179 ns_b = gnc_tree_model_price_get_namespace (model, &iter_b);
180 return safe_utf8_collate (gnc_commodity_namespace_get_gui_name (ns_a),
181 gnc_commodity_namespace_get_gui_name (ns_b));
182 }
183
184 comm_a = gnc_tree_model_price_get_commodity (model, &iter_a);
185 comm_b = gnc_tree_model_price_get_commodity (model, &iter_b);
186 return safe_utf8_collate (gnc_commodity_get_mnemonic (comm_a),
187 gnc_commodity_get_mnemonic (comm_b));
188 }
189
190 static gint
default_sort(GNCPrice * price_a,GNCPrice * price_b)191 default_sort (GNCPrice *price_a, GNCPrice *price_b)
192 {
193 gnc_commodity *curr_a, *curr_b;
194 time64 time_a, time_b;
195 gint result;
196
197 /* Primary sort (i.e. commodity name) handled by the tree structure. */
198
199 /* secondary sort: currency */
200 curr_a = gnc_price_get_currency (price_a);
201 curr_b = gnc_price_get_currency (price_b);
202
203 result = safe_utf8_collate (gnc_commodity_get_namespace (curr_a),
204 gnc_commodity_get_namespace (curr_b));
205 if (result != 0) return result;
206
207 result = safe_utf8_collate (gnc_commodity_get_mnemonic (curr_a),
208 gnc_commodity_get_mnemonic (curr_b));
209 if (result != 0) return result;
210
211 /* tertiary sort: time */
212 time_a = gnc_price_get_time64 (price_a);
213 time_b = gnc_price_get_time64 (price_b);
214 result = time_a < time_b ? -1 : time_a > time_b ? 1 : 0;
215 if (result)
216 /* Reverse the result to present the most recent quote first. */
217 return -result;
218
219 /* last sort: value */
220 return gnc_numeric_compare (gnc_price_get_value (price_a),
221 gnc_price_get_value (price_b));
222 }
223
224 static gint
sort_by_name(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)225 sort_by_name (GtkTreeModel *f_model,
226 GtkTreeIter *f_iter_a,
227 GtkTreeIter *f_iter_b,
228 gpointer user_data)
229 {
230 GNCPrice *price_a, *price_b;
231
232 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
233 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
234
235 return default_sort (price_a, price_b);
236 }
237
238 static gint
sort_by_date(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)239 sort_by_date (GtkTreeModel *f_model,
240 GtkTreeIter *f_iter_a,
241 GtkTreeIter *f_iter_b,
242 gpointer user_data)
243 {
244 GNCPrice *price_a, *price_b;
245 time64 time_a, time_b;
246 gboolean result;
247
248 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
249 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
250
251 /* sort by time first */
252 time_a = gnc_price_get_time64 (price_a);
253 time_b = gnc_price_get_time64 (price_b);
254 result = time_a < time_b ? -1 : time_a > time_b ? 1 : 0;
255 if (result)
256 /* Reverse the result to present the most recent quote first. */
257 return -result;
258
259 return default_sort (price_a, price_b);
260 }
261
262 static gint
sort_by_source(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)263 sort_by_source (GtkTreeModel *f_model,
264 GtkTreeIter *f_iter_a,
265 GtkTreeIter *f_iter_b,
266 gpointer user_data)
267 {
268 GNCPrice *price_a, *price_b;
269 gint result;
270
271 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
272 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
273
274 /* sort by source first */
275 result = gnc_price_get_source (price_a) < gnc_price_get_source (price_b);
276 if (result != 0)
277 return result;
278
279 return default_sort (price_a, price_b);
280 }
281
282 static gint
sort_by_type(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)283 sort_by_type (GtkTreeModel *f_model,
284 GtkTreeIter *f_iter_a,
285 GtkTreeIter *f_iter_b,
286 gpointer user_data)
287 {
288 GNCPrice *price_a, *price_b;
289 gint result;
290
291 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
292 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
293
294 /* sort by source first */
295 result = safe_utf8_collate (gnc_price_get_typestr (price_a),
296 gnc_price_get_typestr (price_b));
297 if (result != 0)
298 return result;
299
300 return default_sort (price_a, price_b);
301 }
302
303 static gint
sort_by_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)304 sort_by_value (GtkTreeModel *f_model,
305 GtkTreeIter *f_iter_a,
306 GtkTreeIter *f_iter_b,
307 gpointer user_data)
308 {
309 gnc_commodity *comm_a, *comm_b;
310 GNCPrice *price_a, *price_b;
311 gboolean result;
312 gint value;
313
314 if (!get_prices (f_model, f_iter_a, f_iter_b, &price_a, &price_b))
315 return sort_ns_or_cm (f_model, f_iter_a, f_iter_b);
316
317 /*
318 * Sorted by commodity because of the tree structure. Now sort by
319 * currency so we're only comparing numbers in the same currency
320 * denomination.
321 */
322 comm_a = gnc_price_get_currency (price_a);
323 comm_b = gnc_price_get_currency (price_b);
324 if (comm_a && comm_b)
325 {
326 value = safe_utf8_collate (gnc_commodity_get_namespace (comm_a),
327 gnc_commodity_get_namespace (comm_b));
328 if (value != 0)
329 return value;
330 value = safe_utf8_collate (gnc_commodity_get_mnemonic (comm_a),
331 gnc_commodity_get_mnemonic (comm_b));
332 if (value != 0)
333 return value;
334 }
335
336 /*
337 * Now do the actual price comparison now we're sure that its an
338 * apples to apples comparison.
339 */
340 result = gnc_numeric_compare (gnc_price_get_value (price_a),
341 gnc_price_get_value (price_b));
342 if (result)
343 return result;
344
345 return default_sort (price_a, price_b);
346 }
347
348
349 /************************************************************/
350 /* New View Creation */
351 /************************************************************/
352
353 /*
354 * Create a new price tree view with (optional) top level root node.
355 * This view will be based on a model that is common to all view of
356 * the same set of books, but will have its own private filter on that
357 * model.
358 */
359 GtkTreeView *
gnc_tree_view_price_new(QofBook * book,const gchar * first_property_name,...)360 gnc_tree_view_price_new (QofBook *book,
361 const gchar *first_property_name,
362 ...)
363 {
364 GncTreeView *view;
365 GtkTreeModel *model, *f_model, *s_model;
366 GtkTreeViewColumn *col;
367 GNCPriceDB *price_db;
368 va_list var_args;
369 const gchar *sample_text;
370 gchar *sample_text2;
371
372 ENTER(" ");
373 /* Create/get a pointer to the existing model for this set of books. */
374 price_db = gnc_pricedb_get_db(book);
375 model = gnc_tree_model_price_new (book, price_db);
376
377 /* Set up the view private filter on the common model. */
378 f_model = gtk_tree_model_filter_new (model, NULL);
379 g_object_unref(G_OBJECT(model));
380 s_model = gtk_tree_model_sort_new_with_model (f_model);
381 g_object_unref(G_OBJECT(f_model));
382
383 /* Create our view */
384 view = g_object_new (GNC_TYPE_TREE_VIEW_PRICE,
385 "name", "gnc-id-price-tree", NULL);
386 gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
387 g_object_unref(G_OBJECT(s_model));
388
389 DEBUG("model ref count is %d", G_OBJECT(model)->ref_count);
390 DEBUG("f_model ref count is %d", G_OBJECT(f_model)->ref_count);
391 DEBUG("s_model ref count is %d", G_OBJECT(s_model)->ref_count);
392
393 sample_text = gnc_commodity_get_printname(gnc_default_currency());
394 sample_text2 = g_strdup_printf("%s%s", sample_text, sample_text);
395 gnc_tree_view_add_text_column (
396 view, _("Security"), "security", NULL, sample_text2,
397 GNC_TREE_MODEL_PRICE_COL_COMMODITY,
398 GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
399 sort_by_name);
400 g_free(sample_text2);
401 col = gnc_tree_view_add_text_column (
402 view, _("Currency"), "currency", NULL, sample_text,
403 GNC_TREE_MODEL_PRICE_COL_CURRENCY,
404 GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
405 sort_by_name);
406 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
407 col = gnc_tree_view_add_text_column (
408 view, _("Date"), "date", NULL, "2005-05-20",
409 GNC_TREE_MODEL_PRICE_COL_DATE,
410 GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
411 sort_by_date);
412 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
413 col = gnc_tree_view_add_text_column (
414 view, _("Source"), "source", NULL, "Finance::Quote",
415 GNC_TREE_MODEL_PRICE_COL_SOURCE,
416 GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
417 sort_by_source);
418 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
419 col = gnc_tree_view_add_text_column (
420 view, _("Type"), "type", NULL, "last",
421 GNC_TREE_MODEL_PRICE_COL_TYPE,
422 GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
423 sort_by_type);
424 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
425 col = gnc_tree_view_add_numeric_column (
426 view, _("Price"), "price", "100.00000",
427 GNC_TREE_MODEL_PRICE_COL_VALUE,
428 GNC_TREE_VIEW_COLUMN_COLOR_NONE,
429 GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
430 sort_by_value);
431 g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
432
433 gnc_tree_view_configure_columns(view);
434
435 /* Set properties */
436 va_start (var_args, first_property_name);
437 g_object_set_valist (G_OBJECT(view), first_property_name, var_args);
438 va_end (var_args);
439
440 /* Sort on the commodity column by default. This allows for a consistent
441 * sort if commodities are removed and re-added from the model. */
442 if (!gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(s_model),
443 NULL, NULL))
444 {
445 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
446 GNC_TREE_MODEL_PRICE_COL_COMMODITY,
447 GTK_SORT_ASCENDING);
448 }
449
450 gtk_widget_show(GTK_WIDGET(view));
451 LEAVE(" %p", view);
452 return GTK_TREE_VIEW(view);
453 }
454
455 /************************************************************/
456 /* Auxiliary Functions */
457 /************************************************************/
458
459 #define debug_path(fn, path) { \
460 gchar *path_string = gtk_tree_path_to_string(path); \
461 fn("tree path %s", path_string); \
462 g_free(path_string); \
463 }
464
465 #if 0 /* Not Used */
466 static gboolean
467 gnc_tree_view_price_get_iter_from_price (GncTreeViewPrice *view,
468 GNCPrice *price,
469 GtkTreeIter *s_iter)
470 {
471 GtkTreeModel *model, *f_model, *s_model;
472 GtkTreeIter iter, f_iter;
473
474 g_return_val_if_fail(GNC_IS_TREE_VIEW_PRICE(view), FALSE);
475 g_return_val_if_fail(price != NULL, FALSE);
476 g_return_val_if_fail(s_iter != NULL, FALSE);
477
478 ENTER("view %p, price %p", view, price);
479
480 /* Reach down to the real model and get an iter for this price */
481 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
482 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
483 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
484 if (!gnc_tree_model_price_get_iter_from_price (GNC_TREE_MODEL_PRICE(model), price, &iter))
485 {
486 LEAVE("model_get_iter_from_price failed");
487 return FALSE;
488 }
489
490 /* convert back to a sort iter */
491 gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER(f_model),
492 &f_iter, &iter);
493 gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model),
494 s_iter, &f_iter);
495 LEAVE(" ");
496 return TRUE;
497 }
498 #endif /* Not Used */
499
500 /************************************************************/
501 /* Price Tree View Filter Functions */
502 /************************************************************/
503
504 /************************************************************/
505 /* Price Tree View Visibility Filter */
506 /************************************************************/
507
508 typedef struct
509 {
510 gnc_tree_view_price_ns_filter_func user_ns_fn;
511 gnc_tree_view_price_cm_filter_func user_cm_fn;
512 gnc_tree_view_price_pc_filter_func user_pc_fn;
513 gpointer user_data;
514 GDestroyNotify user_destroy;
515 } filter_user_data;
516
517 static void
gnc_tree_view_price_filter_destroy(gpointer data)518 gnc_tree_view_price_filter_destroy (gpointer data)
519 {
520 filter_user_data *fd = data;
521
522 if (fd->user_destroy)
523 fd->user_destroy(fd->user_data);
524 g_free(fd);
525 }
526
527 static gboolean
gnc_tree_view_price_filter_helper(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)528 gnc_tree_view_price_filter_helper (GtkTreeModel *model,
529 GtkTreeIter *iter,
530 gpointer data)
531 {
532 gnc_commodity_namespace *name_space;
533 gnc_commodity *commodity;
534 GNCPrice *price;
535 filter_user_data *fd = data;
536
537 g_return_val_if_fail (GNC_IS_TREE_MODEL_PRICE (model), FALSE);
538 g_return_val_if_fail (iter != NULL, FALSE);
539
540 if (gnc_tree_model_price_iter_is_namespace (GNC_TREE_MODEL_PRICE(model), iter))
541 {
542 if (fd->user_ns_fn)
543 {
544 name_space = gnc_tree_model_price_get_namespace (GNC_TREE_MODEL_PRICE(model), iter);
545 return fd->user_ns_fn(name_space, fd->user_data);
546 }
547 return TRUE;
548 }
549
550 if (gnc_tree_model_price_iter_is_commodity (GNC_TREE_MODEL_PRICE(model), iter))
551 {
552 if (fd->user_cm_fn)
553 {
554 commodity = gnc_tree_model_price_get_commodity (GNC_TREE_MODEL_PRICE(model), iter);
555 return fd->user_cm_fn(commodity, fd->user_data);
556 }
557 return TRUE;
558 }
559
560 if (gnc_tree_model_price_iter_is_price (GNC_TREE_MODEL_PRICE(model), iter))
561 {
562 if (fd->user_pc_fn)
563 {
564 price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model), iter);
565 return fd->user_pc_fn(price, fd->user_data);
566 }
567 return TRUE;
568 }
569
570 return FALSE;
571 }
572
573 /*
574 * Set an GtkTreeModel visible filter on this price. This filter will be
575 * called for each price that the tree is about to show, and the
576 * price will be passed to the callback function.
577 */
578 void
gnc_tree_view_price_set_filter(GncTreeViewPrice * view,gnc_tree_view_price_ns_filter_func ns_func,gnc_tree_view_price_cm_filter_func cm_func,gnc_tree_view_price_pc_filter_func pc_func,gpointer data,GDestroyNotify destroy)579 gnc_tree_view_price_set_filter (GncTreeViewPrice *view,
580 gnc_tree_view_price_ns_filter_func ns_func,
581 gnc_tree_view_price_cm_filter_func cm_func,
582 gnc_tree_view_price_pc_filter_func pc_func,
583 gpointer data,
584 GDestroyNotify destroy)
585 {
586 GtkTreeModel *f_model, *s_model;
587 filter_user_data *fd = data;
588
589 ENTER("view %p, ns func %p, cm func %p, pc func %p, data %p, destroy %p",
590 view, ns_func, cm_func, pc_func, data, destroy);
591
592 g_return_if_fail(GNC_IS_TREE_VIEW_PRICE(view));
593 g_return_if_fail((ns_func != NULL) || (cm_func != NULL));
594
595 fd = g_malloc(sizeof(filter_user_data));
596 fd->user_ns_fn = ns_func;
597 fd->user_cm_fn = cm_func;
598 fd->user_pc_fn = pc_func;
599 fd->user_data = data;
600 fd->user_destroy = destroy;
601
602 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
603 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
604
605 /* disconnect model from view */
606 g_object_ref (G_OBJECT(s_model));
607 gtk_tree_view_set_model (GTK_TREE_VIEW(view), NULL);
608
609 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model),
610 gnc_tree_view_price_filter_helper,
611 fd,
612 gnc_tree_view_price_filter_destroy);
613
614 /* Whack any existing levels. The top two levels have been created
615 * before this routine can be called. Unfortunately, if the just
616 * applied filter filters out all the nodes in the tree, the gtk
617 * code throws a critical error. This occurs when there are no
618 * prices in the price database. Once the very first price has been
619 * added this error message goes away. */
620 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model));
621
622 /* connect model to view */
623 gtk_tree_view_set_model (GTK_TREE_VIEW(view), s_model);
624 g_object_unref (G_OBJECT(s_model));
625
626 LEAVE(" ");
627 }
628
629 /************************************************************/
630 /* Price Tree View Get/Set Functions */
631 /************************************************************/
632
633 /*
634 * Retrieve the selected price from an price tree view. The
635 * price tree must be in single selection mode.
636 */
637 GNCPrice *
gnc_tree_view_price_get_selected_price(GncTreeViewPrice * view)638 gnc_tree_view_price_get_selected_price (GncTreeViewPrice *view)
639 {
640 GtkTreeSelection *selection;
641 GtkTreeModel *model, *f_model, *s_model;
642 GtkTreeIter iter, f_iter, s_iter;
643 GNCPrice *price;
644
645 ENTER("view %p", view);
646 g_return_val_if_fail (GNC_IS_TREE_VIEW_PRICE (view), NULL);
647
648 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
649 if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter))
650 {
651 LEAVE("no price, get_selected failed");
652 return FALSE;
653 }
654
655 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
656 &f_iter, &s_iter);
657
658 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
659 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
660 &iter, &f_iter);
661
662 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
663 price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model),
664 &iter);
665 LEAVE("price %p", price);
666 return price;
667 }
668
669 /*
670 * Selects a single price in the price tree view. The price
671 * tree must be in single selection mode.
672 */
673 void
gnc_tree_view_price_set_selected_price(GncTreeViewPrice * view,GNCPrice * price)674 gnc_tree_view_price_set_selected_price (GncTreeViewPrice *view,
675 GNCPrice *price)
676 {
677 GtkTreeModel *model, *f_model, *s_model;
678 GtkTreePath *path, *f_path, *s_path, *parent_path;
679 GtkTreeSelection *selection;
680
681 ENTER("view %p, price %p", view, price);
682
683 /* Clear any existing selection. */
684 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
685 gtk_tree_selection_unselect_all (selection);
686
687 if (price == NULL)
688 return;
689
690 s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
691 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
692 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
693
694 path = gnc_tree_model_price_get_path_from_price (GNC_TREE_MODEL_PRICE(model), price);
695 if (path == NULL)
696 {
697 LEAVE("get_path_from_price failed");
698 return;
699 }
700 debug_path(DEBUG, path);
701
702 f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model),
703 path);
704 gtk_tree_path_free(path);
705 if (f_path == NULL)
706 {
707 LEAVE("no filter path");
708 return;
709 }
710 debug_path(DEBUG, f_path);
711
712 s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
713 f_path);
714 gtk_tree_path_free(f_path);
715 if (s_path == NULL)
716 {
717 LEAVE("no sort path");
718 return;
719 }
720
721 /* gtk_tree_view requires that a row be visible before it can be selected */
722 parent_path = gtk_tree_path_copy (s_path);
723 if (gtk_tree_path_up (parent_path))
724 {
725 /* This function is misnamed. It expands the actual item
726 * specified, not the path to the item specified. I.E. It expands
727 * one level too many, thus the get of the parent. */
728 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
729 }
730 gtk_tree_path_free(parent_path);
731
732 gtk_tree_selection_select_path (selection, s_path);
733 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
734 debug_path(LEAVE, s_path);
735 gtk_tree_path_free(s_path);
736 }
737
738 /*
739 * This helper function is called once for each row in the tree view
740 * that is currently selected. Its task is to add the corresponding
741 * price to the end of a glist.
742 */
743 static void
get_selected_prices_helper(GtkTreeModel * s_model,GtkTreePath * s_path,GtkTreeIter * s_iter,gpointer data)744 get_selected_prices_helper (GtkTreeModel *s_model,
745 GtkTreePath *s_path,
746 GtkTreeIter *s_iter,
747 gpointer data)
748 {
749 GList **return_list = data;
750 GtkTreeModel *model, *f_model;
751 GtkTreeIter iter, f_iter;
752 GNCPrice *price;
753
754 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
755 &f_iter, s_iter);
756
757 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
758 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
759 &iter, &f_iter);
760
761 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
762 price = gnc_tree_model_price_get_price (GNC_TREE_MODEL_PRICE(model),
763 &iter);
764 if (price)
765 *return_list = g_list_append(*return_list, price);
766 }
767
768 /*
769 * Given a price tree view, return a list of the selected prices. The
770 * price tree must be in multiple selection mode.
771 *
772 * Note: It is the responsibility of the caller to free the returned
773 * list.
774 */
775 GList *
gnc_tree_view_price_get_selected_prices(GncTreeViewPrice * view)776 gnc_tree_view_price_get_selected_prices (GncTreeViewPrice *view)
777 {
778 GtkTreeSelection *selection;
779 GList *return_list = NULL;
780
781 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
782 gtk_tree_selection_selected_foreach(selection, get_selected_prices_helper, &return_list);
783 return return_list;
784 }
785
786 static void
get_selected_commodity_helper(GtkTreeModel * s_model,GtkTreePath * s_path,GtkTreeIter * s_iter,gpointer data)787 get_selected_commodity_helper (GtkTreeModel *s_model,
788 GtkTreePath *s_path,
789 GtkTreeIter *s_iter,
790 gpointer data)
791 {
792 GList **return_list = data;
793 GtkTreeModel *model, *f_model;
794 GtkTreeIter iter, f_iter;
795 gnc_commodity *commodity;
796
797 gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
798 &f_iter, s_iter);
799
800 f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
801 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
802 &iter, &f_iter);
803
804 model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
805 commodity = gnc_tree_model_price_get_commodity (GNC_TREE_MODEL_PRICE(model), &iter);
806
807 if (commodity)
808 *return_list = g_list_append(*return_list, commodity);
809 }
810
811 /*
812 * Given a price tree view, return a list of the selected rows that have
813 * commodities but are not prices, the parent rows for prices. The
814 * price tree must be in multiple selection mode.
815 *
816 * Note: It is the responsibility of the caller to free the returned
817 * list.
818 */
819 GList *
gnc_tree_view_price_get_selected_commodities(GncTreeViewPrice * view)820 gnc_tree_view_price_get_selected_commodities (GncTreeViewPrice *view)
821 {
822 GtkTreeSelection *selection;
823 GList *return_list = NULL;
824
825 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
826 gtk_tree_selection_selected_foreach (selection, get_selected_commodity_helper, &return_list);
827 return return_list;
828 }
829