1 /**********************************************************************\
2  * gnc-tree-view-account.c -- GtkTreeView implementation to display   *
3  *                            accounts in a GtkTreeView.              *
4  * Copyright (C) 2003,2005,2006 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-account.h"
33 #include "gnc-tree-model-account-types.h"
34 #include "gnc-tree-view-account.h"
35 
36 #include "Account.h"
37 #include "gnc-accounting-period.h"
38 #include "gnc-commodity.h"
39 #include "gnc-component-manager.h"
40 #include "gnc-engine.h"
41 #include "gnc-glib-utils.h"
42 #include "gnc-gobject-utils.h"
43 #include "gnc-prefs.h"
44 #include "gnc-hooks.h"
45 #include "gnc-session.h"
46 #include "gnc-icons.h"
47 #include "gnc-ui-balances.h"
48 #include "dialog-utils.h"
49 #include "window-main-summarybar.h"
50 
51 #define SAMPLE_ACCOUNT_VALUE   "$1,000,000.00"
52 #define GNC_PREF_ACCOUNT_COLOR "show-account-color"
53 
54 /** Static Globals *******************************************************/
55 
56 /* This static indicates the debugging module that this .o belongs to.  */
57 static QofLogModule log_module = GNC_MOD_GUI;
58 
59 /** Declarations *********************************************************/
60 static void gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass);
61 static void gnc_tree_view_account_init (GncTreeViewAccount *view);
62 static void gnc_tree_view_account_finalize (GObject *object);
63 static gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
64         const gchar *key, GtkTreeIter *iter, gpointer search_data);
65 
66 static void gtva_update_column_names (GncTreeView *view);
67 static void gtva_currency_changed_cb (void);
68 
69 static gboolean gnc_tree_view_account_filter_helper (GtkTreeModel *model,
70         GtkTreeIter *iter,
71         gpointer data);
72 
73 static void gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
74         GtkTreeViewColumn *column,
75         GtkCellRenderer *renderer,
76         GncTreeViewAccountColumnTextEdited col_edited_cb);
77 
78 static void tax_info_data_func (GtkTreeViewColumn *col,
79                                 GtkCellRenderer   *renderer,
80                                 GtkTreeModel      *model,
81                                 GtkTreeIter       *iter,
82                                 gpointer           view);
83 
84 static void acc_color_data_func (GtkTreeViewColumn *col,
85                                  GtkCellRenderer   *renderer,
86                                  GtkTreeModel      *model,
87                                  GtkTreeIter       *iter,
88                                  gpointer           view);
89 
90 static void gnc_tree_view_account_color_update (gpointer gsettings,
91          gchar *key, gpointer user_data);
92 
93 
94 typedef struct GncTreeViewAccountPrivate
95 {
96     AccountViewInfo avi;
97 
98     gnc_tree_view_account_filter_func filter_fn;
99     gpointer                          filter_data;
100     GSourceFunc                       filter_destroy;
101 
102     GtkTreeViewColumn *name_column;
103     GtkTreeViewColumn *code_column;
104     GtkTreeViewColumn *desc_column;
105     GtkTreeViewColumn *present_report_column;
106     GtkTreeViewColumn *balance_report_column;
107     GtkTreeViewColumn *cleared_report_column;
108     GtkTreeViewColumn *reconciled_report_column;
109     GtkTreeViewColumn *future_min_report_column;
110     GtkTreeViewColumn *total_report_column;
111     GtkTreeViewColumn *notes_column;
112 
113     gboolean show_account_color;
114 
115 } GncTreeViewAccountPrivate;
116 
117 #define GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(o)  \
118    ((GncTreeViewAccountPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_TREE_VIEW_ACCOUNT))
119 
120 
121 /************************************************************/
122 /*               g_object required functions                */
123 /************************************************************/
124 
125 static GObjectClass *parent_class = NULL;
126 
G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewAccount,gnc_tree_view_account,GNC_TYPE_TREE_VIEW)127 G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewAccount, gnc_tree_view_account, GNC_TYPE_TREE_VIEW)
128 
129 static void
130 gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass)
131 {
132     GObjectClass *o_class;
133 
134     parent_class = g_type_class_peek_parent (klass);
135 
136     /* GObject signals */
137     o_class = G_OBJECT_CLASS (klass);
138     o_class->finalize = gnc_tree_view_account_finalize;
139 
140     gnc_hook_add_dangler(HOOK_CURRENCY_CHANGED,
141                          (GFunc)gtva_currency_changed_cb, NULL, NULL);
142 }
143 
144 /********************************************************************\
145  * gnc_init_account_view_info                                       *
146  *   initialize an account view info structure with default values  *
147  *                                                                  *
148  * Args: avi - structure to initialize                              *
149  * Returns: nothing                                                 *
150 \********************************************************************/
151 static void
gnc_init_account_view_info(AccountViewInfo * avi)152 gnc_init_account_view_info(AccountViewInfo *avi)
153 {
154     int i;
155 
156     for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
157         avi->include_type[i] = TRUE;
158     avi->show_hidden = FALSE;
159 }
160 
161 static void
gnc_tree_view_account_init(GncTreeViewAccount * view)162 gnc_tree_view_account_init (GncTreeViewAccount *view)
163 {
164     GncTreeViewAccountPrivate *priv;
165 
166     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
167 
168     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
169                            GNC_PREF_ACCOUNT_COLOR,
170                            gnc_tree_view_account_color_update,
171                            view);
172 
173     gnc_init_account_view_info(&priv->avi);
174 }
175 
176 static void
gnc_tree_view_account_finalize(GObject * object)177 gnc_tree_view_account_finalize (GObject *object)
178 {
179     GncTreeViewAccount *account_view;
180     GncTreeViewAccountPrivate *priv;
181 
182     ENTER("view %p", object);
183     g_return_if_fail (object != NULL);
184     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (object));
185 
186     account_view = GNC_TREE_VIEW_ACCOUNT (object);
187 
188     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
189 
190     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
191                                  GNC_PREF_ACCOUNT_COLOR,
192                                  gnc_tree_view_account_color_update,
193                                  account_view);
194     if (priv->filter_destroy)
195     {
196         priv->filter_destroy(priv->filter_data);
197         priv->filter_destroy = NULL;
198     }
199     priv->filter_fn = NULL;
200 
201     if (G_OBJECT_CLASS (parent_class)->finalize)
202         (* G_OBJECT_CLASS (parent_class)->finalize) (object);
203     LEAVE(" ");
204 }
205 
206 
207 /************************************************************
208  *                        Callbacks                         *
209  ************************************************************/
210 static void
gnc_tree_view_account_hidden_toggled(GtkCellRendererToggle * cell,const gchar * s_path_str,gpointer user_data)211 gnc_tree_view_account_hidden_toggled (GtkCellRendererToggle *cell,
212                                       const gchar *s_path_str,
213                                       gpointer user_data)
214 {
215     GncTreeViewAccount *tree_view;
216     GtkTreePath *s_path;
217     Account *account;
218     gboolean hidden;
219 
220     /* Change the requested account */
221     tree_view = user_data;
222     s_path = gtk_tree_path_new_from_string (s_path_str);
223     account = gnc_tree_view_account_get_account_from_path (tree_view, s_path);
224     if (account)
225     {
226         hidden = !gtk_cell_renderer_toggle_get_active (cell); // hasn't changed yet.
227         xaccAccountSetHidden (account, hidden);
228     }
229 
230     /* Clean up */
231     gtk_tree_path_free (s_path);
232 }
233 
234 
235 static void
gnc_tree_view_account_placeholder_toggled(GtkCellRendererToggle * cell,const gchar * s_path_str,gpointer user_data)236 gnc_tree_view_account_placeholder_toggled (GtkCellRendererToggle *cell,
237         const gchar *s_path_str,
238         gpointer user_data)
239 {
240     GncTreeViewAccount *tree_view;
241     GtkTreePath *s_path;
242     Account *account;
243     gboolean placeholder;
244 
245     /* Change the requested account */
246     tree_view = user_data;
247     s_path = gtk_tree_path_new_from_string (s_path_str);
248     account = gnc_tree_view_account_get_account_from_path (tree_view, s_path);
249     if (account)
250     {
251         placeholder = !gtk_cell_renderer_toggle_get_active (cell); // hasn't changed yet.
252         xaccAccountSetPlaceholder (account, placeholder);
253     }
254 
255     /* Clean up */
256     gtk_tree_path_free (s_path);
257 }
258 
259 
260 /************************************************************/
261 /*                      sort functions                      */
262 /************************************************************/
263 
264 static GtkTreeModel *
sort_cb_setup_w_iters(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,GtkTreeIter * iter_a,GtkTreeIter * iter_b,const Account ** account_a,const Account ** account_b)265 sort_cb_setup_w_iters (GtkTreeModel *f_model,
266                        GtkTreeIter *f_iter_a,
267                        GtkTreeIter *f_iter_b,
268                        GtkTreeIter *iter_a,
269                        GtkTreeIter *iter_b,
270                        const Account **account_a,
271                        const Account **account_b)
272 {
273     GtkTreeModel *model;
274 
275     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
276     gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
277             iter_a,
278             f_iter_a);
279     gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
280             iter_b,
281             f_iter_b);
282     *account_a = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_a);
283     *account_b = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_b);
284     return model;
285 }
286 
287 static void
sort_cb_setup(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,const Account ** account_a,const Account ** account_b)288 sort_cb_setup (GtkTreeModel *f_model,
289                GtkTreeIter *f_iter_a,
290                GtkTreeIter *f_iter_b,
291                const Account **account_a,
292                const Account **account_b)
293 {
294     GtkTreeIter iter_a, iter_b;
295 
296     sort_cb_setup_w_iters (f_model, f_iter_a, f_iter_b,
297                            &iter_a, &iter_b, account_a, account_b);
298 }
299 
300 static gint
sort_by_last_reconcile_date(GtkTreeModel * f_model,GtkTreeIter * f_iter1,GtkTreeIter * f_iter2,gpointer user_data)301 sort_by_last_reconcile_date (GtkTreeModel *f_model,
302                              GtkTreeIter *f_iter1,
303                              GtkTreeIter *f_iter2,
304                              gpointer user_data)
305 {
306     const Account *account1, *account2;
307     time64 account1_date, account2_date;
308 
309     sort_cb_setup (f_model, f_iter1, f_iter2, &account1, &account2);
310 
311     if (!xaccAccountGetReconcileLastDate (account1, &account1_date))
312         account1_date = 0;
313 
314     if (!xaccAccountGetReconcileLastDate (account2, &account2_date))
315         account2_date = 0;
316 
317     if (account1_date < account2_date)
318         return -1;
319     else if (account1_date > account2_date)
320         return 1;
321     else
322         return xaccAccountOrder (account1, account2);
323 }
324 
325 static gint
sort_by_string(GtkTreeModel * f_model,GtkTreeIter * f_iter1,GtkTreeIter * f_iter2,gpointer user_data)326 sort_by_string (GtkTreeModel *f_model,
327                 GtkTreeIter *f_iter1,
328                 GtkTreeIter *f_iter2,
329                 gpointer user_data)
330 {
331     GtkTreeModel *model;
332     GtkTreeIter iter1, iter2;
333     const Account *account1, *account2;
334     gchar *str1, *str2;
335     gint column = GPOINTER_TO_INT(user_data);
336     gint result;
337 
338     model = sort_cb_setup_w_iters(f_model, f_iter1, f_iter2, &iter1, &iter2, &account1, &account2);
339 
340     /* Get the strings. */
341     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter1,  column, &str1, -1);
342     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter2,  column, &str2, -1);
343 
344     result = safe_utf8_collate(str1, str2);
345     g_free(str1);
346     g_free(str2);
347     if (result != 0)
348         return result;
349     return xaccAccountOrder(account1, account2);
350 }
351 
352 static gint
sort_by_code(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)353 sort_by_code (GtkTreeModel *f_model,
354               GtkTreeIter *f_iter_a,
355               GtkTreeIter *f_iter_b,
356               gpointer user_data)
357 {
358     const Account *account_a, *account_b;
359 
360     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
361 
362     /* Default ordering uses this column first. */
363     return xaccAccountOrder(account_a, account_b);
364 }
365 
366 static gint
sort_by_xxx_value(xaccGetBalanceInCurrencyFn fn,gboolean recurse,GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)367 sort_by_xxx_value (xaccGetBalanceInCurrencyFn fn,
368                    gboolean recurse,
369                    GtkTreeModel *f_model,
370                    GtkTreeIter *f_iter_a,
371                    GtkTreeIter *f_iter_b,
372                    gpointer user_data)
373 {
374     const Account *account_a, *account_b;
375     const gnc_commodity *cur = gnc_default_currency();
376     gnc_numeric balance_a, balance_b;
377     gint result;
378 
379     /* Find the accounts */
380     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
381 
382     /* Get balances */
383     balance_a = gnc_ui_account_get_balance_full(fn, account_a, recurse, NULL, cur);
384     balance_b = gnc_ui_account_get_balance_full(fn, account_b, recurse, NULL, cur);
385 
386     result = gnc_numeric_compare(balance_a, balance_b);
387     if (result != 0)
388         return result;
389     return xaccAccountOrder(account_a, account_b);
390 }
391 
392 static gint
sort_by_present_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)393 sort_by_present_value (GtkTreeModel *f_model,
394                        GtkTreeIter *f_iter_a,
395                        GtkTreeIter *f_iter_b,
396                        gpointer user_data)
397 {
398     return sort_by_xxx_value (xaccAccountGetPresentBalanceInCurrency, TRUE,
399                               f_model, f_iter_a, f_iter_b, user_data);
400 }
401 
402 static gint
sort_by_balance_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)403 sort_by_balance_value (GtkTreeModel *f_model,
404                        GtkTreeIter *f_iter_a,
405                        GtkTreeIter *f_iter_b,
406                        gpointer user_data)
407 {
408     return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
409                               f_model, f_iter_a, f_iter_b, user_data);
410 }
411 
412 static gint
sort_by_cleared_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)413 sort_by_cleared_value (GtkTreeModel *f_model,
414                        GtkTreeIter *f_iter_a,
415                        GtkTreeIter *f_iter_b,
416                        gpointer user_data)
417 {
418     return sort_by_xxx_value (xaccAccountGetClearedBalanceInCurrency, TRUE,
419                               f_model, f_iter_a, f_iter_b, user_data);
420 }
421 
422 static gint
sort_by_reconciled_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)423 sort_by_reconciled_value (GtkTreeModel *f_model,
424                           GtkTreeIter *f_iter_a,
425                           GtkTreeIter *f_iter_b,
426                           gpointer user_data)
427 {
428     return sort_by_xxx_value (xaccAccountGetReconciledBalanceInCurrency, TRUE,
429                               f_model, f_iter_a, f_iter_b, user_data);
430 }
431 
432 static gint
sort_by_future_min_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)433 sort_by_future_min_value (GtkTreeModel *f_model,
434                           GtkTreeIter *f_iter_a,
435                           GtkTreeIter *f_iter_b,
436                           gpointer user_data)
437 {
438     return sort_by_xxx_value (xaccAccountGetProjectedMinimumBalanceInCurrency, TRUE,
439                               f_model, f_iter_a, f_iter_b, user_data);
440 }
441 
442 static gint
sort_by_total_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)443 sort_by_total_value (GtkTreeModel *f_model,
444                      GtkTreeIter *f_iter_a,
445                      GtkTreeIter *f_iter_b,
446                      gpointer user_data)
447 {
448     return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
449                               f_model, f_iter_a, f_iter_b, user_data);
450 }
451 
452 static gint
sort_by_hidden(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)453 sort_by_hidden (GtkTreeModel *f_model,
454                 GtkTreeIter *f_iter_a,
455                 GtkTreeIter *f_iter_b,
456                 gpointer user_data)
457 {
458     const Account *account_a, *account_b;
459     gboolean flag_a, flag_b;
460 
461     /* Find the accounts */
462     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
463 
464     /* Get the placeholder flags. */
465     flag_a = xaccAccountGetHidden (account_a);
466     flag_b = xaccAccountGetHidden (account_b);
467 
468     if (flag_a > flag_b)
469         return -1;
470     else if (flag_a < flag_b)
471         return 1;
472     return xaccAccountOrder (account_a, account_b);
473 }
474 
475 static gint
sort_by_placeholder(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)476 sort_by_placeholder (GtkTreeModel *f_model,
477                      GtkTreeIter *f_iter_a,
478                      GtkTreeIter *f_iter_b,
479                      gpointer user_data)
480 {
481     const Account *account_a, *account_b;
482     gboolean flag_a, flag_b;
483 
484     /* Find the accounts */
485     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
486 
487     /* Get the placeholder flags. */
488     flag_a = xaccAccountGetPlaceholder(account_a);
489     flag_b = xaccAccountGetPlaceholder(account_b);
490 
491     if (flag_a > flag_b)
492         return -1;
493     else if (flag_a < flag_b)
494         return 1;
495     return xaccAccountOrder(account_a, account_b);
496 }
497 
498 static gint
sort_by_opening_balance(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)499 sort_by_opening_balance (GtkTreeModel *f_model,
500                          GtkTreeIter *f_iter_a,
501                          GtkTreeIter *f_iter_b,
502                          gpointer user_data)
503 {
504     const Account *account_a, *account_b;
505     gboolean flag_a, flag_b;
506 
507     /* Find the accounts */
508     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
509 
510     /* Get the opening balance flags. */
511     flag_a = xaccAccountGetIsOpeningBalance (account_a);
512     flag_b = xaccAccountGetIsOpeningBalance (account_b);
513 
514     if (flag_a > flag_b)
515         return -1;
516     else if (flag_a < flag_b)
517         return 1;
518     return xaccAccountOrder(account_a, account_b);
519 }
520 
521 static gint
sort_by_xxx_period_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gboolean recurse)522 sort_by_xxx_period_value (GtkTreeModel *f_model,
523                           GtkTreeIter *f_iter_a,
524                           GtkTreeIter *f_iter_b,
525                           gboolean recurse)
526 {
527     Account *acct1, *acct2;
528     time64 t1, t2;
529     gnc_numeric b1, b2;
530     gint result;
531 
532     sort_cb_setup (f_model, f_iter_a, f_iter_b,
533                    (const Account **)&acct1, (const Account **)&acct2);
534 
535     t1 = gnc_accounting_period_fiscal_start();
536     t2 = gnc_accounting_period_fiscal_end();
537 
538     b1 = xaccAccountGetBalanceChangeForPeriod(acct1, t1, t2, recurse);
539     b2 = xaccAccountGetBalanceChangeForPeriod(acct2, t1, t2, recurse);
540 
541     result = gnc_numeric_compare(b1, b2);
542     if (result != 0)
543         return result;
544     return xaccAccountOrder(acct1, acct2);
545 }
546 
547 static gint
sort_by_balance_period_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)548 sort_by_balance_period_value (GtkTreeModel *f_model,
549                               GtkTreeIter *f_iter_a,
550                               GtkTreeIter *f_iter_b,
551                               gpointer user_data)
552 {
553     return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, FALSE);
554 }
555 
556 static gint
sort_by_total_period_value(GtkTreeModel * f_model,GtkTreeIter * f_iter_a,GtkTreeIter * f_iter_b,gpointer user_data)557 sort_by_total_period_value (GtkTreeModel *f_model,
558                             GtkTreeIter *f_iter_a,
559                             GtkTreeIter *f_iter_b,
560                             gpointer user_data)
561 {
562     return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, TRUE);
563 }
564 
565 /************************************************************/
566 /*                 Tax_Info data function                   */
567 /************************************************************/
568 
569 /*
570  * The tax-info column in the account tree view is based on the
571  * combination of two columns in the account tree model. The data
572  * function displays only the the data in the
573  * GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO model column if the row is
574  * expanded; otherwise it combines it with the data
575  * in the GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT model column.
576  */
577 static void
tax_info_data_func(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer view)578 tax_info_data_func (GtkTreeViewColumn *col,
579                     GtkCellRenderer   *renderer,
580                     GtkTreeModel      *model,
581                     GtkTreeIter       *iter,
582                     gpointer           view)
583 {
584     gchar *tax_info = NULL;
585     GtkTreePath *path;
586 
587     gtk_tree_model_get(model,
588                        iter,
589                        GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
590                        &tax_info,
591                        -1);
592 
593     path = gtk_tree_model_get_path(model, iter);
594     if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(view), path))
595         g_object_set(renderer, "text",
596                      (tax_info == NULL ? "" : tax_info), NULL);
597     else
598     {
599         gchar *tax_info_sub_acct = NULL;
600 
601         gtk_tree_model_get(model,
602                            iter,
603                            GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT,
604                            &tax_info_sub_acct,
605                            -1);
606         if ((g_strcmp0 (tax_info_sub_acct, "") == 0) ||
607                 (tax_info_sub_acct == NULL))
608             g_object_set(renderer, "text",
609                          (tax_info == NULL ? "" : tax_info), NULL);
610         else
611         {
612             if ((g_strcmp0 (tax_info, "") == 0) ||
613                     (tax_info == NULL))
614                 g_object_set(renderer, "text",
615                              (tax_info_sub_acct == NULL ? "" : tax_info_sub_acct),
616                              NULL);
617             else
618             {
619                 gchar *combined_tax_info;
620                 combined_tax_info = g_strdup_printf ("%s; %s",
621                                                      (tax_info == NULL ? "" : tax_info),
622                                                      (tax_info_sub_acct == NULL ? "" :
623                                                       tax_info_sub_acct));
624                 g_object_set(renderer, "text", combined_tax_info, NULL);
625                 g_free(combined_tax_info);
626             }
627         }
628         g_free(tax_info_sub_acct);
629     }
630     g_free(tax_info);
631     gtk_tree_path_free(path);
632 }
633 
634 /************************************************************/
635 /*                acc_color data function                   */
636 /************************************************************/
637 /*
638  * The account-color column in the account tree view is obtained
639  * from the GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT which is
640  * checked for a valid color string to set the background color
641  * of the cell.
642  */
643 static void
update_cell_renderers(GList * renderers,gchar * account_color)644 update_cell_renderers (GList *renderers, gchar *account_color)
645 {
646     GtkCellRenderer *cell;
647     GList *node;
648 
649     /* Update the cell background in the list of renderers */
650     for (node = renderers; node; node = node->next)
651     {
652         cell = node->data;
653         g_object_set (cell, "cell-background", account_color, NULL);
654     }
655 }
656 
657 /* Colorizes a cell in the account tree view if
658  * - a color is assigned to the given account
659  * - the user enabled account colorization in the preferences
660  * Only the account color column is special: it will always
661  * be colored if a valid color was set, regardless of the
662  * preference setting.
663  */
664 static void
acc_color_data_func(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer view)665 acc_color_data_func (GtkTreeViewColumn *col,
666                      GtkCellRenderer   *renderer,
667                      GtkTreeModel      *model,
668                      GtkTreeIter       *iter,
669                      gpointer           view)
670 {
671     GncTreeViewAccountPrivate *priv;
672     gchar                     *acc_color = NULL, *acc_cond_color = NULL;
673     gchar                     *item;
674     GdkRGBA                    color;
675     gchar                     *column_name;
676     GList                     *renderers;
677 
678     gtk_tree_model_get(model,
679                        iter,
680                        GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT,
681                        &item,
682                        -1);
683 
684     /* Check if color was set for the account */
685     if ((item) && (*item != '\0'))
686         acc_color = g_strstrip(g_strdup(item));
687     g_free (item);
688 
689     /* Test if the color string represents a valid color */
690     if (acc_color && (!gdk_rgba_parse(&color, acc_color)))
691     {
692         g_free (acc_color);
693         acc_color = NULL;
694     }
695 
696     /* Determine whether columns other than the
697      * Account Color column should be colored. */
698     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
699     if (priv->show_account_color)
700         acc_cond_color = acc_color;
701 
702     column_name = g_object_get_data(G_OBJECT(col), PREF_NAME);
703     renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
704 
705     /* Account Color column is always colored, other columns only conditionally. */
706     if (g_strcmp0(column_name, "account-color") == 0)
707         update_cell_renderers (renderers, acc_color);
708     else
709         update_cell_renderers (renderers, acc_cond_color);
710 
711     g_list_free (renderers);
712     g_free (acc_color);
713 }
714 
715 /** Tell the GncTreeViewAccount code to show or not show the
716  *  Account name column with background in the account color.
717  *
718  *  @internal
719  */
720 static void
gnc_tree_view_account_color_update(gpointer gsettings,gchar * key,gpointer user_data)721 gnc_tree_view_account_color_update (gpointer gsettings, gchar *key, gpointer user_data)
722 {
723     GncTreeViewAccountPrivate *priv;
724     GncTreeViewAccount *view;
725 
726     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(user_data));
727     view = user_data;
728     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
729     if (g_strcmp0 (key, GNC_PREF_ACCOUNT_COLOR) == 0)
730         priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, key);
731 
732     // do a refilter so the tree view background color gets updated
733     gnc_tree_view_account_refilter (view);
734 }
735 
736 /** Add the account color background data function to the GncTreeViewAccount column to
737  *  show or not the column background in the account color.
738  */
739 void
gnc_tree_view_account_column_add_color(GncTreeViewAccount * view,GtkTreeViewColumn * col)740 gnc_tree_view_account_column_add_color (GncTreeViewAccount *view, GtkTreeViewColumn *col)
741 {
742     GtkCellRenderer *renderer = gnc_tree_view_column_get_renderer(col);
743 
744     gtk_tree_view_column_set_cell_data_func (col, renderer, acc_color_data_func,
745                                              GTK_TREE_VIEW(view), NULL);
746 }
747 
748 /************************************************************/
749 /*                    New View Creation                     */
750 /************************************************************/
751 
752 /*
753  * Create a new account tree view with (optional) top level root node.
754  * This view will be based on a model that is common to all view of
755  * the same set of books, but will have its own private filter on that
756  * model.
757  */
758 GtkTreeView *
gnc_tree_view_account_new_with_root(Account * root,gboolean show_root)759 gnc_tree_view_account_new_with_root (Account *root, gboolean show_root)
760 {
761     GncTreeView *view;
762     GtkTreeModel *model, *f_model, *s_model;
763     GtkTreePath *virtual_root_path = NULL;
764     const gchar *sample_type, *sample_commodity;
765     GncTreeViewAccountPrivate *priv;
766     GtkTreeViewColumn *tax_info_column, *acc_color_column;
767     GtkCellRenderer *renderer;
768     GList *col_list = NULL, *node = NULL;
769 
770     ENTER(" ");
771     /* Create our view */
772     view = g_object_new (GNC_TYPE_TREE_VIEW_ACCOUNT,
773                          "name", "gnc-id-account-tree", NULL);
774 
775     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(GNC_TREE_VIEW_ACCOUNT (view));
776 
777     /* Get the show_account_color value from gsettings */
778     priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_COLOR);
779 
780     /* Create/get a pointer to the existing model for this set of books. */
781     model = gnc_tree_model_account_new (root);
782 
783     /* Set up the view private filter layer on the common model. */
784     if (!show_root)
785         virtual_root_path = gtk_tree_path_new_first ();
786     f_model = gtk_tree_model_filter_new (model, virtual_root_path);
787     /* A GncTreeModelAccount is based on a GncTreeModel, which is a
788      * GObject that provides a GtkTreeModel interface. */
789     g_object_unref(G_OBJECT(model));
790     if (virtual_root_path)
791         gtk_tree_path_free(virtual_root_path);
792 
793     /* Set up the view private sort layer on the common model. */
794     s_model = gtk_tree_model_sort_new_with_model(f_model);
795     g_object_unref(G_OBJECT(f_model));
796     gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
797     g_object_unref(G_OBJECT(s_model));
798 
799     /* Set default visibilities */
800     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(view), FALSE);
801 
802     sample_type = xaccAccountGetTypeStr(ACCT_TYPE_CREDIT);
803     sample_commodity = gnc_commodity_get_fullname(gnc_default_currency());
804 
805     priv->name_column
806         = gnc_tree_view_add_text_column(view, _("Account Name"), "name",
807                                         GNC_ICON_ACCOUNT, "Expenses:Entertainment",
808                                         GNC_TREE_MODEL_ACCOUNT_COL_NAME,
809                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
810                                         sort_by_string);
811 
812     gnc_tree_view_add_text_column(view, _("Type"), "type", NULL, sample_type,
813                                   GNC_TREE_MODEL_ACCOUNT_COL_TYPE,
814                                   GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
815                                   sort_by_string);
816 
817     gnc_tree_view_add_text_column(view, _("Commodity"), "commodity", NULL,
818                                   sample_commodity,
819                                   GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY,
820                                   GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
821                                   sort_by_string);
822     priv->code_column
823         = gnc_tree_view_add_text_column(view, _("Account Code"), "account-code", NULL,
824                                         "1-123-1234",
825                                         GNC_TREE_MODEL_ACCOUNT_COL_CODE,
826                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
827                                         sort_by_code);
828     priv->desc_column
829         = gnc_tree_view_add_text_column(view, _("Description"), "description", NULL,
830                                         "Sample account description.",
831                                         GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,
832                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
833                                         sort_by_string);
834 
835     gnc_tree_view_add_numeric_column(view, _("Last Num"), "lastnum", "12345",
836                                      GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM,
837                                      GNC_TREE_VIEW_COLUMN_COLOR_NONE,
838                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
839                                      sort_by_string);
840 
841     gnc_tree_view_add_numeric_column(view, _("Present"), "present",
842                                      SAMPLE_ACCOUNT_VALUE,
843                                      GNC_TREE_MODEL_ACCOUNT_COL_PRESENT,
844                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
845                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
846                                      sort_by_present_value);
847     priv->present_report_column
848         = gnc_tree_view_add_numeric_column(view, _("Present (Report)"), "present_report",
849                                            SAMPLE_ACCOUNT_VALUE,
850                                            GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT,
851                                            GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
852                                            GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
853                                            sort_by_present_value);
854 
855     gnc_tree_view_add_numeric_column(view, _("Balance"), "balance",
856                                      SAMPLE_ACCOUNT_VALUE,
857                                      GNC_TREE_MODEL_ACCOUNT_COL_BALANCE,
858                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
859                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
860                                      sort_by_balance_value);
861     priv->balance_report_column
862         = gnc_tree_view_add_numeric_column(view, _("Balance (Report)"), "balance_report",
863                                            SAMPLE_ACCOUNT_VALUE,
864                                            GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT,
865                                            GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
866                                            GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
867                                            sort_by_balance_value);
868 
869     gnc_tree_view_add_numeric_column(view, _("Balance (Period)"), "balance-period",
870                                      SAMPLE_ACCOUNT_VALUE,
871                                      GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD,
872                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD,
873                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
874                                      sort_by_balance_period_value);
875 
876     gnc_tree_view_add_numeric_column(view, _("Cleared"), "cleared",
877                                      SAMPLE_ACCOUNT_VALUE,
878                                      GNC_TREE_MODEL_ACCOUNT_COL_CLEARED,
879                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
880                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
881                                      sort_by_cleared_value);
882     priv->cleared_report_column
883         = gnc_tree_view_add_numeric_column(view, _("Cleared (Report)"), "cleared_report",
884                                            SAMPLE_ACCOUNT_VALUE,
885                                            GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT,
886                                            GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
887                                            GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
888                                            sort_by_cleared_value);
889 
890     gnc_tree_view_add_numeric_column(view, _("Reconciled"), "reconciled",
891                                      SAMPLE_ACCOUNT_VALUE,
892                                      GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED,
893                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
894                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
895                                      sort_by_reconciled_value);
896     priv->reconciled_report_column
897         = gnc_tree_view_add_numeric_column(view, _("Reconciled (Report)"), "reconciled_report",
898                                            SAMPLE_ACCOUNT_VALUE,
899                                            GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT,
900                                            GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
901                                            GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
902                                            sort_by_reconciled_value);
903 
904     gnc_tree_view_add_text_column(view, _("Last Reconcile Date"), "last-recon-date", NULL,
905                                   "Last Reconcile Date",
906                                   GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE,
907                                   GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
908                                   sort_by_last_reconcile_date);
909 
910     gnc_tree_view_add_numeric_column(view, _("Future Minimum"), "future_min",
911                                      SAMPLE_ACCOUNT_VALUE,
912                                      GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN,
913                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
914                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
915                                      sort_by_future_min_value);
916     priv->future_min_report_column
917         =  gnc_tree_view_add_numeric_column(view, _("Future Minimum (Report)"), "future_min_report",
918                                             SAMPLE_ACCOUNT_VALUE,
919                                             GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT,
920                                             GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
921                                             GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
922                                             sort_by_future_min_value);
923 
924     gnc_tree_view_add_numeric_column(view, _("Total"), "total",
925                                      SAMPLE_ACCOUNT_VALUE,
926                                      GNC_TREE_MODEL_ACCOUNT_COL_TOTAL,
927                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
928                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
929                                      sort_by_total_value);
930     priv->total_report_column
931         = gnc_tree_view_add_numeric_column(view, _("Total (Report)"), "total_report",
932                                            SAMPLE_ACCOUNT_VALUE,
933                                            GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT,
934                                            GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
935                                            GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
936                                            sort_by_total_value);
937 
938     gnc_tree_view_add_numeric_column(view, _("Total (Period)"), "total-period",
939                                      SAMPLE_ACCOUNT_VALUE,
940                                      GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD,
941                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD,
942                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
943                                      sort_by_total_period_value);
944 
945     /* Translators: The C is the column title and stands for Color, this should be one character */
946     acc_color_column
947         = gnc_tree_view_add_text_column(view, C_("Column header for 'Color'", "C"), "account-color", NULL,
948                                         "xx",
949                                         GNC_TREE_VIEW_COLUMN_DATA_NONE,
950                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
951                                         NULL);
952 
953     /* Add the full title to the object for menu creation */
954     g_object_set_data_full(G_OBJECT(acc_color_column), REAL_TITLE,
955                            g_strdup(_("Account Color")), g_free);
956 
957     /* Also add the full title to the column header as a tooltip */
958     gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (acc_color_column), _("Account Color"));
959 
960     priv->notes_column
961         = gnc_tree_view_add_text_view_column(view, _("Notes"), "notes", NULL,
962                                         "Sample account notes.",
963                                         GNC_TREE_MODEL_ACCOUNT_COL_NOTES,
964                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
965                                         sort_by_string);
966 
967     tax_info_column
968         = gnc_tree_view_add_text_column(view, _("Tax Info"), "tax-info", NULL,
969                                         "Sample tax info.",
970                                         GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
971                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
972                                         sort_by_string);
973 
974     renderer = gnc_tree_view_column_get_renderer(tax_info_column);
975     gtk_tree_view_column_set_cell_data_func(tax_info_column,
976                                             renderer,
977                                             tax_info_data_func,
978                                             GTK_TREE_VIEW(view),
979                                             NULL);
980 
981     gnc_tree_view_add_toggle_column (view, _("Hidden"),
982                                      C_("Column header for 'Hidden'", "H"),
983                                      "hidden",
984                                      GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN,
985                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
986                                      sort_by_hidden,
987                                      gnc_tree_view_account_hidden_toggled);
988 
989     gnc_tree_view_add_toggle_column(view, _("Placeholder"),
990 				    C_("Column header for 'Placeholder'", "P"),
991                                     "placeholder",
992                                     GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER,
993                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
994                                     sort_by_placeholder,
995                                     gnc_tree_view_account_placeholder_toggled);
996 
997     gnc_tree_view_add_toggle_column(view, _("Opening Balance"),
998                     C_("Column header for 'Opening Balance'", "O"),
999                                     "opening-balance",
1000                                     GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE,
1001                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
1002                                     sort_by_opening_balance,
1003                                     NULL);
1004 
1005     /* Add function to each column that optionally sets a background color for accounts */
1006     col_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
1007     for (node = col_list; node; node = node->next)
1008     {
1009         renderer = gnc_tree_view_column_get_renderer(node->data);
1010         gtk_tree_view_column_set_cell_data_func(node->data,
1011                 renderer,
1012                 acc_color_data_func,
1013                 GTK_TREE_VIEW(view),
1014                 NULL);
1015     }
1016     g_list_free (col_list);
1017 
1018     /* Update column titles to use the currency name. */
1019     gtva_update_column_names(view);
1020 
1021     /* By default only the first column is visible. */
1022     gnc_tree_view_configure_columns(view);
1023     gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model),
1024                                             gnc_tree_view_account_filter_helper,
1025                                             view,
1026                                             NULL);
1027 
1028     /* Default the sorting to account name */
1029     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
1030                                          GNC_TREE_MODEL_ACCOUNT_COL_NAME,
1031                                          GTK_SORT_ASCENDING);
1032 
1033     /* Set account find-as-you-type search function */
1034     gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(view), gnc_tree_view_search_compare, NULL, NULL);
1035 
1036     gtk_widget_show(GTK_WIDGET(view));
1037     LEAVE("%p", view);
1038     return GTK_TREE_VIEW(view);
1039 }
1040 
1041 /*
1042  * Create a new account tree view with (optional) top level root node.
1043  * This view will be based on a model that is common to all view of
1044  * the same set of books, but will have its own private filter on that
1045  * model.
1046  */
1047 GtkTreeView *
gnc_tree_view_account_new(gboolean show_root)1048 gnc_tree_view_account_new (gboolean show_root)
1049 {
1050     Account *root;
1051 
1052     root = gnc_book_get_root_account (gnc_get_current_book ());
1053     return gnc_tree_view_account_new_with_root (root, show_root);
1054 }
1055 
1056 /************************************************************/
1057 /*                   Auxiliary Functions                    */
1058 /************************************************************/
1059 
1060 #define debug_path(fn, path) {              \
1061     gchar *path_string = gtk_tree_path_to_string(path); \
1062     fn("tree path %s", path_string);            \
1063     g_free(path_string);                \
1064   }
1065 
1066 static GtkTreePath *
gnc_tree_view_account_get_path_from_account(GncTreeViewAccount * view,Account * account)1067 gnc_tree_view_account_get_path_from_account (GncTreeViewAccount *view,
1068         Account *account)
1069 {
1070     GtkTreeModel *model, *f_model, *s_model;
1071     GtkTreePath *path, *f_path, *s_path;
1072 
1073     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1074 
1075     if (account == NULL)
1076     {
1077         LEAVE("no account");
1078         return NULL;
1079     }
1080 
1081     /* Reach down to the real model and get a path for this account */
1082     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1083     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1084     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1085     path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
1086     if (path == NULL)
1087     {
1088         LEAVE("no path");
1089         return NULL;
1090     }
1091 
1092     /* convert back to a filtered path */
1093     f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model), path);
1094     gtk_tree_path_free(path);
1095     if (!f_path)
1096     {
1097         LEAVE("no filter path");
1098         return NULL;
1099     }
1100 
1101     /* convert back to a sorted path */
1102     s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model), f_path);
1103     gtk_tree_path_free(f_path);
1104     debug_path(LEAVE, s_path);
1105     return s_path;
1106 }
1107 
1108 static gboolean
gnc_tree_view_account_get_iter_from_account(GncTreeViewAccount * view,Account * account,GtkTreeIter * s_iter)1109 gnc_tree_view_account_get_iter_from_account (GncTreeViewAccount *view,
1110         Account *account,
1111         GtkTreeIter *s_iter)
1112 {
1113     GtkTreeModel *model, *f_model, *s_model;
1114     GtkTreeIter iter, f_iter;
1115 
1116     g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view), FALSE);
1117     g_return_val_if_fail(account != NULL, FALSE);
1118     g_return_val_if_fail(s_iter != NULL, FALSE);
1119 
1120     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1121 
1122     /* Reach down to the real model and get an iter for this account */
1123     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1124     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1125     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1126     if (!gnc_tree_model_account_get_iter_from_account (
1127                 GNC_TREE_MODEL_ACCOUNT(model), account, &iter))
1128     {
1129         LEAVE("model_get_iter_from_account failed");
1130         return FALSE;
1131     }
1132 
1133     /* convert back to a sort iter */
1134     gtk_tree_model_filter_convert_child_iter_to_iter (
1135         GTK_TREE_MODEL_FILTER(f_model), &f_iter, &iter);
1136     gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model),
1137             s_iter, &f_iter);
1138     LEAVE(" ");
1139     return TRUE;
1140 }
1141 
1142 gint
gnc_tree_view_account_count_children(GncTreeViewAccount * view,Account * account)1143 gnc_tree_view_account_count_children (GncTreeViewAccount *view,
1144                                       Account *account)
1145 {
1146     GtkTreeModel *s_model;
1147     GtkTreeIter s_iter;
1148     gint num_children;
1149 
1150     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1151 
1152     if (account == NULL)
1153     {
1154         LEAVE("no account");
1155         return 0;
1156     }
1157 
1158     if (!gnc_tree_view_account_get_iter_from_account (view, account, &s_iter))
1159     {
1160         LEAVE("view_get_iter_from_account failed");
1161         return 0;
1162     }
1163 
1164     /* Any children? */
1165     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1166     num_children = gtk_tree_model_iter_n_children(s_model, &s_iter);
1167     LEAVE("%d children", num_children);
1168     return num_children;
1169 }
1170 
1171 void
gnc_tree_view_account_clear_model_cache(GncTreeViewAccount * view)1172 gnc_tree_view_account_clear_model_cache (GncTreeViewAccount *view)
1173 {
1174     GtkTreeModel *model, *f_model, *s_model;
1175 
1176     s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
1177     f_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(s_model));
1178     model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(f_model));
1179 
1180     gnc_tree_model_account_clear_cache (GNC_TREE_MODEL_ACCOUNT(model));
1181 }
1182 
1183 /************************************************************/
1184 /*            Account Tree View Filter Functions            */
1185 /************************************************************/
1186 
1187 /*
1188  * Get a copy of the account view info structure in use by the
1189  * specified tree.
1190  */
1191 void
gnc_tree_view_account_get_view_info(GncTreeViewAccount * account_view,AccountViewInfo * avi)1192 gnc_tree_view_account_get_view_info (GncTreeViewAccount *account_view,
1193                                      AccountViewInfo *avi)
1194 {
1195     GncTreeViewAccountPrivate *priv;
1196 
1197     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
1198     g_return_if_fail(avi != NULL);
1199 
1200     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
1201 
1202     *avi = priv->avi;
1203 }
1204 
1205 /*
1206  * Set the account view info data in use by the specified tree to
1207  * match the callers request.
1208  *
1209  * DRH - COMPATIBILITY WARNING
1210  *
1211  * This function does not do anything with the 'include_type' field.
1212  * Should there be a automatic filter for backward compatibility
1213  * that uses these flags, or should all uses of this be converted to
1214  * a GtkTreeModelFilter?
1215  *
1216  * CAS - For now, I'll try the automatic filter approach by making
1217  * this function use GtkTreeModelFilter.
1218  */
1219 void
gnc_tree_view_account_set_view_info(GncTreeViewAccount * account_view,AccountViewInfo * avi)1220 gnc_tree_view_account_set_view_info (GncTreeViewAccount *account_view,
1221                                      AccountViewInfo *avi)
1222 {
1223     GncTreeViewAccountPrivate *priv;
1224     gint i;
1225     guint sel_bits = 0;
1226 
1227     ENTER("%p", account_view);
1228     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
1229     g_return_if_fail(avi != NULL);
1230 
1231     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
1232     priv->avi = *avi;
1233 
1234     for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
1235     {
1236         sel_bits |= avi->include_type[i] ? (1 << i) : 0;
1237     }
1238 
1239     gnc_tree_view_account_set_filter(
1240         account_view, gnc_tree_view_account_filter_by_view_info,
1241         &priv->avi, NULL);
1242 
1243     LEAVE(" ");
1244 }
1245 
1246 static gboolean
gnc_tree_view_account_filter_helper(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1247 gnc_tree_view_account_filter_helper (GtkTreeModel *model,
1248                                      GtkTreeIter *iter,
1249                                      gpointer data)
1250 {
1251     Account *account;
1252     GncTreeViewAccount *view = data;
1253     GncTreeViewAccountPrivate *priv;
1254 
1255     g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), FALSE);
1256     g_return_val_if_fail (iter != NULL, FALSE);
1257 
1258     account = gnc_tree_model_account_get_account (
1259                   GNC_TREE_MODEL_ACCOUNT(model), iter);
1260 
1261     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1262     if (priv->filter_fn)
1263         return priv->filter_fn(account, priv->filter_data);
1264     else return TRUE;
1265 }
1266 
1267 /*
1268  * Set an GtkTreeModel visible filter on this account.  This filter will be
1269  * called for each account that the tree is about to show, and the
1270  * account will be passed to the callback function.
1271  *
1272  * Use NULL as func to remove filter.
1273  */
1274 void
gnc_tree_view_account_set_filter(GncTreeViewAccount * view,gnc_tree_view_account_filter_func func,gpointer data,GSourceFunc destroy)1275 gnc_tree_view_account_set_filter (GncTreeViewAccount *view,
1276                                   gnc_tree_view_account_filter_func func,
1277                                   gpointer data,
1278                                   GSourceFunc destroy)
1279 {
1280     GncTreeViewAccountPrivate *priv;
1281 
1282     ENTER("view %p, filter func %p, data %p, destroy %p",
1283           view, func, data, destroy);
1284 
1285     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
1286 
1287     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1288     if (priv->filter_destroy)
1289     {
1290         priv->filter_destroy(priv->filter_data);
1291     }
1292     priv->filter_destroy = destroy;
1293     priv->filter_data = data;
1294     priv->filter_fn = func;
1295 
1296     gnc_tree_view_account_refilter(view);
1297     LEAVE(" ");
1298 }
1299 
1300 /*
1301  * Forces the entire account tree to be re-evaluated for visibility.
1302  */
1303 void
gnc_tree_view_account_refilter(GncTreeViewAccount * view)1304 gnc_tree_view_account_refilter (GncTreeViewAccount *view)
1305 {
1306     GtkTreeModel *f_model, *s_model;
1307 
1308     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
1309 
1310     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1311     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1312     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model));
1313 }
1314 
1315 gboolean
gnc_tree_view_account_filter_by_view_info(Account * acct,gpointer data)1316 gnc_tree_view_account_filter_by_view_info(Account* acct, gpointer data)
1317 {
1318     GNCAccountType acct_type;
1319     AccountViewInfo* avi = (AccountViewInfo*)data;
1320 
1321     g_return_val_if_fail(GNC_IS_ACCOUNT(acct), FALSE);
1322     acct_type = xaccAccountGetType(acct);
1323 
1324     if (!avi->include_type[acct_type]) return FALSE;
1325     if (!avi->show_hidden && xaccAccountIsHidden(acct)) return FALSE;
1326     return TRUE;
1327 }
1328 
1329 /************************************************************/
1330 /*           Account Tree View Get/Set Functions            */
1331 /************************************************************/
1332 
1333 /*
1334  * Retrieve the selected account from an account tree view.  The
1335  * account tree must be in single selection mode.
1336  */
1337 Account *
gnc_tree_view_account_get_account_from_path(GncTreeViewAccount * view,GtkTreePath * s_path)1338 gnc_tree_view_account_get_account_from_path (GncTreeViewAccount *view,
1339         GtkTreePath *s_path)
1340 {
1341     GtkTreeModel *model, *f_model, *s_model;
1342     GtkTreePath *path, *f_path;
1343     GtkTreeIter iter;
1344     Account *account;
1345 
1346     ENTER("view %p", view);
1347     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1348     g_return_val_if_fail (s_path != NULL, NULL);
1349 
1350     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1351     f_path = gtk_tree_model_sort_convert_path_to_child_path (
1352                  GTK_TREE_MODEL_SORT (s_model), s_path);
1353     if (!f_path)
1354     {
1355         LEAVE("no filter path");
1356         return NULL;
1357     }
1358 
1359     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1360     path = gtk_tree_model_filter_convert_path_to_child_path (
1361                GTK_TREE_MODEL_FILTER (f_model), f_path);
1362     gtk_tree_path_free(f_path);
1363     if (!path)
1364     {
1365         LEAVE("no path");
1366         return NULL;
1367     }
1368 
1369     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1370     if (!gtk_tree_model_get_iter (model, &iter, path))
1371     {
1372         LEAVE("no iter");
1373         return NULL;
1374     }
1375 
1376     account = iter.user_data;
1377     gtk_tree_path_free(path);
1378     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1379     return account;
1380 }
1381 
1382 
1383 Account *
gnc_tree_view_account_get_account_from_iter(GtkTreeModel * s_model,GtkTreeIter * s_iter)1384 gnc_tree_view_account_get_account_from_iter (GtkTreeModel *s_model,
1385         GtkTreeIter  *s_iter)
1386 {
1387     GtkTreeModel *model, *f_model;
1388     GtkTreeIter iter, f_iter;
1389     Account *account;
1390 
1391     g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT(s_model), NULL);
1392     g_return_val_if_fail (s_iter != NULL, NULL);
1393 
1394     ENTER("model %p, iter %p", s_model, s_iter);
1395 
1396     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(s_model),
1397             &f_iter,
1398             s_iter);
1399     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1400     gtk_tree_model_filter_convert_iter_to_child_iter (
1401         GTK_TREE_MODEL_FILTER(f_model), &iter, &f_iter);
1402     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1403     account = gnc_tree_model_account_get_account (
1404                   GNC_TREE_MODEL_ACCOUNT(model), &iter);
1405     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1406     return account;
1407 }
1408 
1409 
1410 /*
1411  * Retrieve the selected account from an account tree view.  The
1412  * account tree must be in single selection mode.
1413  */
1414 Account *
gnc_tree_view_account_get_selected_account(GncTreeViewAccount * view)1415 gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view)
1416 {
1417     GtkTreeSelection *selection;
1418     GtkTreeModel *f_model, *s_model;
1419     GtkTreeIter iter, f_iter, s_iter;
1420     Account *account;
1421     GtkSelectionMode mode;
1422 
1423     ENTER("view %p", view);
1424     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1425 
1426     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
1427     mode = gtk_tree_selection_get_mode(selection);
1428     if ((mode != GTK_SELECTION_SINGLE) && (mode != GTK_SELECTION_BROWSE))
1429     {
1430         return NULL;
1431     }
1432     if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter))
1433     {
1434         LEAVE("no account, get_selected failed");
1435         return FALSE;
1436     }
1437 
1438     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
1439             &f_iter, &s_iter);
1440 
1441     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1442     gtk_tree_model_filter_convert_iter_to_child_iter (
1443         GTK_TREE_MODEL_FILTER (f_model), &iter, &f_iter);
1444 
1445     account = iter.user_data;
1446     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1447     return account;
1448 }
1449 
1450 /*
1451  * Selects a single account in the account tree view.  The account
1452  * tree must be in single selection mode.
1453  */
1454 void
gnc_tree_view_account_set_selected_account(GncTreeViewAccount * view,Account * account)1455 gnc_tree_view_account_set_selected_account (GncTreeViewAccount *view,
1456         Account *account)
1457 {
1458     GtkTreeModel *model, *f_model, *s_model;
1459     GtkTreePath *path, *f_path, *s_path, *parent_path;
1460     GtkTreeSelection *selection;
1461 
1462     ENTER("view %p, account %p (%s)", view,
1463           account, xaccAccountGetName (account));
1464     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
1465 
1466     /* Clear any existing selection. */
1467     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1468     gtk_tree_selection_unselect_all (selection);
1469 
1470     if (account == NULL)
1471         return;
1472 
1473     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1474     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1475     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1476 
1477     path = gnc_tree_model_account_get_path_from_account (
1478                GNC_TREE_MODEL_ACCOUNT(model), account);
1479     if (path == NULL)
1480     {
1481         LEAVE("no path");
1482         return;
1483     }
1484     debug_path(DEBUG, path);
1485 
1486     f_path = gtk_tree_model_filter_convert_child_path_to_path (
1487                  GTK_TREE_MODEL_FILTER (f_model), path);
1488     gtk_tree_path_free(path);
1489     if (f_path == NULL)
1490     {
1491         LEAVE("no filter path");
1492         return;
1493     }
1494     debug_path(DEBUG, f_path);
1495 
1496     s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
1497              f_path);
1498     gtk_tree_path_free(f_path);
1499     if (s_path == NULL)
1500     {
1501         LEAVE("no sort path");
1502         return;
1503     }
1504 
1505     /* gtk_tree_view requires that a row be visible before it can be selected */
1506     parent_path = gtk_tree_path_copy (s_path);
1507     if (gtk_tree_path_up (parent_path))
1508     {
1509         /* This function is misnamed.  It expands the actual item
1510          * specified, not the path to the item specified. I.E. It expands
1511          * one level too many, thus the get of the parent. */
1512         gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
1513     }
1514     gtk_tree_path_free(parent_path);
1515 
1516     gtk_tree_selection_select_path (selection, s_path);
1517 
1518     /* give gtk+ a chance to resize the tree view first by handling pending
1519      * configure events */
1520     while (gtk_events_pending ())
1521         gtk_main_iteration ();
1522     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
1523     debug_path(LEAVE, s_path);
1524     gtk_tree_path_free(s_path);
1525 }
1526 
1527 /* Information re selection process */
1528 typedef struct
1529 {
1530     GList* return_list;
1531     GncTreeViewAccountPrivate* priv;
1532 } GncTreeViewSelectionInfo;
1533 
1534 /*
1535  * This helper function is called once for each row in the tree view
1536  * that is currently selected.  Its task is to append the corresponding
1537  * account to the end of a glist.
1538  */
1539 static void
get_selected_accounts_helper(GtkTreeModel * s_model,GtkTreePath * s_path,GtkTreeIter * s_iter,gpointer data)1540 get_selected_accounts_helper (GtkTreeModel *s_model,
1541                               GtkTreePath *s_path,
1542                               GtkTreeIter *s_iter,
1543                               gpointer data)
1544 {
1545     GncTreeViewSelectionInfo *gtvsi = data;
1546     GtkTreeModel *f_model;
1547     GtkTreeIter iter, f_iter;
1548     Account *account;
1549 
1550     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
1551             &f_iter, s_iter);
1552 
1553     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1554     gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
1555             &iter, &f_iter);
1556     account = iter.user_data;
1557 
1558     /* Only selected if it passes the filter */
1559     if (gtvsi->priv->filter_fn == NULL || gtvsi->priv->filter_fn(account, gtvsi->priv->filter_data))
1560     {
1561         gtvsi->return_list = g_list_prepend (gtvsi->return_list, account);
1562     }
1563 }
1564 
1565 /*
1566  * Given an account tree view, return a list of the selected accounts. The
1567  * account tree must be in multiple selection mode.
1568  *
1569  * Note: It is the responsibility of the caller to free the returned
1570  * list.
1571  */
1572 GList *
gnc_tree_view_account_get_selected_accounts(GncTreeViewAccount * view)1573 gnc_tree_view_account_get_selected_accounts (GncTreeViewAccount *view)
1574 {
1575     GtkTreeSelection *selection;
1576     GncTreeViewSelectionInfo info;
1577 
1578     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1579 
1580     info.return_list = NULL;
1581     info.priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1582     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
1583     gtk_tree_selection_selected_foreach(selection, get_selected_accounts_helper, &info);
1584     info.return_list = g_list_reverse (info.return_list);
1585     return info.return_list;
1586 }
1587 
1588 /*
1589  * Given an account tree view and a list of accounts, select those
1590  * accounts in the tree view.
1591  */
1592 void
gnc_tree_view_account_set_selected_accounts(GncTreeViewAccount * view,GList * account_list,gboolean show_last)1593 gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view,
1594         GList *account_list,
1595         gboolean show_last)
1596 {
1597     GtkTreeModel *model, *f_model, *s_model;
1598     GtkTreePath *path, *f_path, *s_path, *parent_path;
1599     GtkTreeSelection *selection;
1600     GList *element;
1601     Account *account;
1602 
1603     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
1604 
1605     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1606     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
1607     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
1608 
1609     /* Clear any existing selection. */
1610     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1611     gtk_tree_selection_unselect_all (selection);
1612     gtk_tree_view_collapse_all (GTK_TREE_VIEW(view));
1613 
1614     /* Now go select what the user requested. */
1615     for (element = account_list; element; )
1616     {
1617         account = element->data;
1618         element = g_list_next(element);
1619 
1620         if (account == NULL)
1621         {
1622             /*
1623              * Oops.  Someone must have deleted this account and not cleaned
1624              * up all references to it.
1625              */
1626             continue;
1627         }
1628 
1629         path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
1630         if (path == NULL)
1631         {
1632             /*
1633              * Oops.  Someone must have deleted this account and not cleaned
1634              * up all references to it.
1635              */
1636             continue;
1637         }
1638 
1639         f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model),
1640                  path);
1641         gtk_tree_path_free(path);
1642         if (f_path == NULL)
1643             continue;
1644 
1645         s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
1646                  f_path);
1647         gtk_tree_path_free(f_path);
1648         if (s_path == NULL)
1649             continue;
1650 
1651         /* gtk_tree_view requires that a row be visible before it can be selected */
1652         parent_path = gtk_tree_path_copy (s_path);
1653         if (gtk_tree_path_up (parent_path))
1654         {
1655             /* This function is misnamed.  It expands the actual item
1656              * specified, not the path to the item specified. I.E. It
1657              * expands one level too many, thus the get of the parent. */
1658             gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
1659         }
1660         gtk_tree_path_free(parent_path);
1661 
1662         gtk_tree_selection_select_path (selection, s_path);
1663         if (show_last && (element == NULL))
1664             gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
1665         gtk_tree_path_free(s_path);
1666     }
1667 }
1668 
1669 /*
1670  * Selects all sub-accounts of an account.
1671  */
1672 void
gnc_tree_view_account_select_subaccounts(GncTreeViewAccount * view,Account * account)1673 gnc_tree_view_account_select_subaccounts (GncTreeViewAccount *view,
1674         Account *account)
1675 {
1676     GtkTreeModel *s_model;
1677     GtkTreeSelection *selection;
1678     GtkTreePath *sp_account, *sp_start, *sp_end;
1679     GtkTreeIter si_account, si_start, si_end;
1680     gboolean have_start, have_end = FALSE;
1681     gint num_children;
1682 
1683     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
1684 
1685     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
1686 
1687     if (account == NULL)
1688     {
1689         LEAVE("no account");
1690         return;
1691     }
1692 
1693     if (!gnc_tree_view_account_get_iter_from_account (view, account, &si_account))
1694     {
1695         LEAVE("view_get_iter_from_account failed");
1696         return;
1697     }
1698 
1699     /* Any children? */
1700     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1701     num_children = gtk_tree_model_iter_n_children(s_model, &si_account);
1702     if (num_children == 0)
1703     {
1704         LEAVE("no children");
1705         return;
1706     }
1707 
1708     /* Expand the tree.  Required for selection to work */
1709     sp_account = gtk_tree_model_get_path (s_model, &si_account);
1710     gtk_tree_view_expand_row (GTK_TREE_VIEW(view), sp_account, TRUE);
1711 
1712     /* compute start/end paths */
1713     have_start = gtk_tree_model_iter_nth_child(s_model, &si_start, &si_account, 0);
1714     si_end = si_account;
1715     while (num_children)
1716     {
1717         GtkTreeIter tmp_iter = si_end;
1718         have_end = gtk_tree_model_iter_nth_child(s_model, &si_end, &tmp_iter,
1719                    num_children - 1);
1720         if (have_end)
1721             num_children = gtk_tree_model_iter_n_children(s_model, &si_end);
1722         else
1723             num_children = 0;
1724     }
1725 
1726     if (have_start && have_end)
1727     {
1728         sp_start = gtk_tree_model_get_path (s_model, &si_start);
1729         sp_end = gtk_tree_model_get_path (s_model, &si_end);
1730 
1731         /* select everything between */
1732         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1733         gtk_tree_selection_select_range (selection, sp_start, sp_end);
1734 
1735         /* clean up */
1736         gtk_tree_path_free(sp_start);
1737         gtk_tree_path_free(sp_end);
1738     }
1739     gtk_tree_path_free(sp_account);
1740     LEAVE(" ");
1741     return;
1742 }
1743 
1744 void
gnc_tree_view_account_expand_to_account(GncTreeViewAccount * view,Account * account)1745 gnc_tree_view_account_expand_to_account (GncTreeViewAccount *view,
1746         Account *account)
1747 {
1748     GtkTreePath *path;
1749 
1750     g_return_if_fail(view != NULL);
1751     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
1752     ENTER("view %p, account %p", view, account);
1753 
1754     path = gnc_tree_view_account_get_path_from_account(view, account);
1755     if (path)
1756     {
1757         gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), path);
1758         gtk_tree_path_free(path);
1759     }
1760     LEAVE(" ");
1761 }
1762 
1763 
1764 /*
1765  * Retrieve the account currently under the cursor.
1766  */
1767 Account *
gnc_tree_view_account_get_cursor_account(GncTreeViewAccount * view)1768 gnc_tree_view_account_get_cursor_account (GncTreeViewAccount *view)
1769 {
1770     GtkTreePath *s_path;
1771     Account *account;
1772 
1773     ENTER("view %p", view);
1774     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1775 
1776     gtk_tree_view_get_cursor (GTK_TREE_VIEW(view), &s_path, NULL);
1777     if (!s_path)
1778     {
1779         LEAVE("no account");
1780         return NULL;
1781     }
1782 
1783     account = gnc_tree_view_account_get_account_from_path (view, s_path);
1784     gtk_tree_path_free(s_path);
1785     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
1786     return account;
1787 }
1788 
1789 
1790 /************************************************************/
1791 /*         Account Tree View Add Column Functions           */
1792 /************************************************************/
1793 
1794 static void
gtva_update_column_name(GtkTreeViewColumn * column,const gchar * fmt,const gchar * mnemonic)1795 gtva_update_column_name (GtkTreeViewColumn *column,
1796                          const gchar *fmt,
1797                          const gchar *mnemonic)
1798 {
1799     gchar *name;
1800 
1801     g_return_if_fail(column);
1802 
1803     name = g_strdup_printf(fmt, mnemonic);
1804     gtk_tree_view_column_set_title(column, name);
1805     g_free(name);
1806 }
1807 
1808 
1809 static void
gtva_update_column_names(GncTreeView * view)1810 gtva_update_column_names (GncTreeView *view)
1811 {
1812     GncTreeViewAccountPrivate *priv;
1813     const gchar *mnemonic;
1814 
1815     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
1816     mnemonic = gnc_commodity_get_mnemonic(gnc_default_report_currency());
1817 
1818     gtva_update_column_name(priv->present_report_column,
1819                             /* Translators: %s is a currency mnemonic.*/
1820                             _("Present (%s)"), mnemonic);
1821     gtva_update_column_name(priv->balance_report_column,
1822                             /* Translators: %s is a currency mnemonic.*/
1823                             _("Balance (%s)"), mnemonic);
1824     gtva_update_column_name(priv->cleared_report_column,
1825                             /* Translators: %s is a currency mnemonic.*/
1826                             _("Cleared (%s)"), mnemonic);
1827     gtva_update_column_name(priv->reconciled_report_column,
1828                             /* Translators: %s is a currency mnemonic.*/
1829                             _("Reconciled (%s)"), mnemonic);
1830     gtva_update_column_name(priv->future_min_report_column,
1831                             /* Translators: %s is a currency mnemonic.*/
1832                             _("Future Minimum (%s)"), mnemonic);
1833     gtva_update_column_name(priv->total_report_column,
1834                             /* Translators: %s is a currency mnemonic.*/
1835                             _("Total (%s)"), mnemonic);
1836     gnc_tree_view_set_show_column_menu(view, FALSE);
1837     gnc_tree_view_set_show_column_menu(view, TRUE);
1838 }
1839 
1840 
1841 static void
gtva_currency_changed_cb(void)1842 gtva_currency_changed_cb (void)
1843 {
1844     const GList *views, *ptr;
1845 
1846     views = gnc_gobject_tracking_get_list (GNC_TREE_VIEW_ACCOUNT_NAME);
1847     for (ptr = views; ptr; ptr = g_list_next(ptr))
1848     {
1849         gtva_update_column_names (ptr->data);
1850     }
1851 }
1852 /* Retrieve a specified account string property and put the result
1853  * into the tree column's text property.
1854  */
1855 static void
account_cell_property_data_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer key)1856 account_cell_property_data_func (GtkTreeViewColumn *tree_column,
1857                  GtkCellRenderer *cell,
1858                  GtkTreeModel *s_model,
1859                  GtkTreeIter *s_iter,
1860                  gpointer key)
1861 {
1862     GncTreeViewAccount *view;
1863     Account *account;
1864     gchar *string = NULL;
1865 
1866     g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
1867     account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
1868     qof_instance_get (QOF_INSTANCE (account), key, &string, NULL);
1869     if (string == NULL)
1870         string = g_strdup ("");
1871 
1872     g_object_set (G_OBJECT (cell), "text", string, "xalign", 0.0, NULL);
1873     g_free (string);
1874 
1875     view = g_object_get_data(G_OBJECT(tree_column), "tree-view");
1876 
1877     if (GNC_IS_TREE_VIEW_ACCOUNT (view))
1878         acc_color_data_func (tree_column, cell, s_model, s_iter, view);
1879 }
1880 
1881 
1882 GtkTreeViewColumn *
gnc_tree_view_account_add_property_column(GncTreeViewAccount * view,const gchar * column_title,const gchar * propname)1883 gnc_tree_view_account_add_property_column (GncTreeViewAccount *view,
1884                                       const gchar *column_title,
1885                                       const gchar *propname)
1886 {
1887     GtkCellRenderer *renderer;
1888     GtkTreeViewColumn *column;
1889 
1890     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
1891     g_return_val_if_fail (propname != NULL, NULL);
1892 
1893     column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(view), column_title,
1894                                            propname, NULL, "Sample text",
1895                                            -1, -1, NULL);
1896 
1897     /* This new kvp column has only had one renderer added to it so
1898      * far.  Find that renderer. */
1899     renderer = gnc_tree_view_column_get_renderer(column);
1900     g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
1901 
1902     // add a pointer to the view to make it easier to access in data_func
1903     g_object_set_data(G_OBJECT(column), "tree-view", (gpointer)view);
1904 
1905     gtk_tree_view_column_set_cell_data_func (column, renderer,
1906             account_cell_property_data_func,
1907             g_strdup(propname), g_free);
1908     return column;
1909 }
1910 
col_edited_helper(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,gpointer _s_model)1911 static void col_edited_helper(GtkCellRendererText *cell, gchar *path_string,
1912                               gchar *new_text, gpointer _s_model)
1913 {
1914     Account *account;
1915     GtkTreeModel *s_model;
1916     GtkTreeIter s_iter;
1917     GncTreeViewAccountColumnTextEdited col_edited_cb;
1918     GtkTreeViewColumn *col;
1919 
1920     col_edited_cb = g_object_get_data(G_OBJECT(cell),
1921                                       "column_edited_callback");
1922     col = GTK_TREE_VIEW_COLUMN(g_object_get_data(G_OBJECT(cell),
1923                                "column_view"));
1924     s_model = GTK_TREE_MODEL(_s_model);
1925 
1926     if (!gtk_tree_model_get_iter_from_string(s_model, &s_iter, path_string))
1927         return;
1928 
1929     account = gnc_tree_view_account_get_account_from_iter(s_model, &s_iter);
1930     col_edited_cb(account, col, new_text);
1931 }
1932 
col_source_helper(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * s_model,GtkTreeIter * s_iter,gpointer _col_source_cb)1933 static void col_source_helper(GtkTreeViewColumn *col, GtkCellRenderer *cell,
1934                               GtkTreeModel *s_model, GtkTreeIter *s_iter,
1935                               gpointer _col_source_cb)
1936 {
1937     Account *account;
1938     gchar *text;
1939     GncTreeViewAccountColumnSource col_source_cb;
1940 
1941     g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
1942     col_source_cb = (GncTreeViewAccountColumnSource) _col_source_cb;
1943     account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
1944     text = col_source_cb(account, col, cell);
1945     g_object_set (G_OBJECT (cell), "text", text, "xalign", 1.0, NULL);
1946     g_free(text);
1947 }
1948 
1949 /**
1950  * If col_edited_cb is null, the editing callback (helper) will be
1951  * effectively disconnected.
1952  **/
1953 void
gtva_setup_column_renderer_edited_cb(GncTreeViewAccount * account_view,GtkTreeViewColumn * column,GtkCellRenderer * renderer,GncTreeViewAccountColumnTextEdited col_edited_cb)1954 gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
1955                                      GtkTreeViewColumn *column,
1956                                      GtkCellRenderer *renderer,
1957                                      GncTreeViewAccountColumnTextEdited col_edited_cb)
1958 {
1959     GtkTreeModel *s_model;
1960 
1961     if (col_edited_cb == NULL)
1962     {
1963         g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL);
1964         g_object_set_data(G_OBJECT(renderer), "column_edited_callback", col_edited_cb);
1965         s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
1966         g_signal_handlers_disconnect_by_func(G_OBJECT(renderer), col_edited_cb, s_model);
1967         g_object_set_data(G_OBJECT(renderer), "column_view", column);
1968     }
1969     else
1970     {
1971         g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
1972         g_object_set_data(G_OBJECT(renderer), "column_edited_callback",
1973                           col_edited_cb);
1974         s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
1975         g_signal_connect(G_OBJECT(renderer), "edited",
1976                          (GCallback) col_edited_helper, s_model);
1977         g_object_set_data(G_OBJECT(renderer), "column_view", column);
1978     }
1979 }
1980 
1981 GtkTreeViewColumn *
gnc_tree_view_account_add_custom_column(GncTreeViewAccount * account_view,const gchar * column_title,GncTreeViewAccountColumnSource col_source_cb,GncTreeViewAccountColumnTextEdited col_edited_cb)1982 gnc_tree_view_account_add_custom_column(GncTreeViewAccount *account_view,
1983                                         const gchar *column_title,
1984                                         GncTreeViewAccountColumnSource
1985                                         col_source_cb,
1986                                         GncTreeViewAccountColumnTextEdited
1987                                         col_edited_cb)
1988 {
1989     GtkCellRenderer *renderer;
1990 
1991     g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view), NULL);
1992 
1993     renderer = gtk_cell_renderer_text_new();
1994 
1995     return gnc_tree_view_account_add_custom_column_renderer(
1996         account_view, column_title, col_source_cb, col_edited_cb, renderer);
1997 }
1998 
1999 GtkTreeViewColumn *
gnc_tree_view_account_add_custom_column_renderer(GncTreeViewAccount * account_view,const gchar * column_title,GncTreeViewAccountColumnSource col_source_cb,GncTreeViewAccountColumnTextEdited col_edited_cb,GtkCellRenderer * renderer)2000 gnc_tree_view_account_add_custom_column_renderer(GncTreeViewAccount *account_view,
2001                                         const gchar *column_title,
2002                                         GncTreeViewAccountColumnSource
2003                                         col_source_cb,
2004                                         GncTreeViewAccountColumnTextEdited
2005                                         col_edited_cb,
2006                                         GtkCellRenderer *renderer)
2007 {
2008     GtkTreeViewColumn *column;
2009 
2010     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (account_view), NULL);
2011 
2012     g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
2013 
2014     column = gtk_tree_view_column_new_with_attributes (column_title,
2015              renderer, NULL);
2016     if (col_edited_cb)
2017     {
2018         gtva_setup_column_renderer_edited_cb(account_view, column,
2019                                              renderer, col_edited_cb);
2020     }
2021     gtk_tree_view_column_set_cell_data_func (column, renderer,
2022             col_source_helper,
2023             col_source_cb, NULL);
2024     gnc_tree_view_append_column (GNC_TREE_VIEW(account_view), column);
2025     return column;
2026 }
2027 
2028 
2029 /* BEGIN FILTER FUNCTIONS */
2030 #define FILTER_TREE_VIEW "types_tree_view"
2031 
2032 /** This function tells the account tree view whether or not to filter
2033  *  out a particular account.  Accounts may be filtered if the user
2034  *  has decided not to display that particular account type, or if the
2035  *  user has requested that accounts with a zero total not be shown.
2036  *
2037  *  @param account The account that was toggled.
2038  *
2039  *  @param user_data A pointer to the AccountFilterDialog struct.
2040  *
2041  *  @return TRUE if the account should be visible.  FALSE if the
2042  *  account should be hidden. */
2043 gboolean
gnc_plugin_page_account_tree_filter_accounts(Account * account,gpointer user_data)2044 gnc_plugin_page_account_tree_filter_accounts (Account *account,
2045         gpointer user_data)
2046 {
2047     AccountFilterDialog *fd = user_data;
2048     GNCAccountType acct_type;
2049     gnc_numeric total;
2050     gboolean result;
2051 
2052     ENTER("account %p:%s", account, xaccAccountGetName(account));
2053 
2054     if (g_hash_table_size (fd->filter_override) > 0)
2055     {
2056         Account *test_acc = NULL;
2057         test_acc = g_hash_table_lookup (fd->filter_override, account);
2058         if (test_acc != NULL)
2059         {
2060             LEAVE(" filter: override");
2061             return TRUE;
2062         }
2063     }
2064 
2065     if (!fd->show_hidden && xaccAccountIsHidden (account))
2066     {
2067         LEAVE(" hide: hidden");
2068         return FALSE;
2069     }
2070 
2071     if (!fd->show_zero_total)
2072     {
2073         total = xaccAccountGetBalanceInCurrency (account, NULL, TRUE);
2074         if (gnc_numeric_zero_p(total))
2075         {
2076             LEAVE(" hide: zero balance");
2077             return FALSE;
2078         }
2079     }
2080 
2081     if (!fd->show_unused)
2082     {
2083         if (gnc_account_and_descendants_empty(account))
2084         {
2085             LEAVE(" hide: unused");
2086             return FALSE;
2087         }
2088     }
2089 
2090     acct_type = xaccAccountGetType(account);
2091     result = (fd->visible_types & (1 << acct_type)) ? TRUE : FALSE;
2092     LEAVE(" %s", result ? "show" : "hide");
2093     return result;
2094 }
2095 
2096 /** The "show hidden" button in the Filter dialog changed state.
2097  *  Update the page to reflect these changes.
2098  *
2099  *  @param button The GtkCheckButton that was toggled.
2100  *
2101  *  @param fd A pointer to the account filter dialog struct.
2102  */
2103 void
gppat_filter_show_hidden_toggled_cb(GtkToggleButton * button,AccountFilterDialog * fd)2104 gppat_filter_show_hidden_toggled_cb (GtkToggleButton *button,
2105                                      AccountFilterDialog *fd)
2106 {
2107     g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
2108 
2109     ENTER("button %p", button);
2110     fd->show_hidden = gtk_toggle_button_get_active(button);
2111     gnc_tree_view_account_refilter(fd->tree_view);
2112     LEAVE("show_hidden %d", fd->show_hidden);
2113 }
2114 
2115 /** The "show zero totals" button in the Filter dialog changed state.
2116  *  Update the page to reflect these changes.
2117  *
2118  *  @param button The GtkCheckButton that was toggled.
2119  *
2120  *  @param fd A pointer to the account filter dialog struct.
2121  */
2122 void
gppat_filter_show_zero_toggled_cb(GtkToggleButton * button,AccountFilterDialog * fd)2123 gppat_filter_show_zero_toggled_cb (GtkToggleButton *button,
2124                                    AccountFilterDialog *fd)
2125 {
2126     g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
2127 
2128     ENTER("button %p", button);
2129     fd->show_zero_total = gtk_toggle_button_get_active(button);
2130     gnc_tree_view_account_refilter(fd->tree_view);
2131     LEAVE("show_zero %d", fd->show_zero_total);
2132 }
2133 
2134 /** The "show unused" button in the Filter dialog changed state.
2135  *  Update the page to reflect these changes.
2136  *
2137  *  @param button The GtkCheckButton that was toggled.
2138  *
2139  *  @param fd A pointer to the account filter dialog struct.
2140  */
2141 void
gppat_filter_show_unused_toggled_cb(GtkToggleButton * button,AccountFilterDialog * fd)2142 gppat_filter_show_unused_toggled_cb (GtkToggleButton *button,
2143                                    AccountFilterDialog *fd)
2144 {
2145     g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
2146 
2147     ENTER("button %p", button);
2148     fd->show_unused = gtk_toggle_button_get_active(button);
2149     gnc_tree_view_account_refilter(fd->tree_view);
2150     LEAVE("show_unused %d", fd->show_unused);
2151 }
2152 
2153 /** The "clear all account types" button in the Filter dialog was
2154  *  clicked.  Clear all account types shown, and update the visible
2155  *  page.
2156  *
2157  *  @param button The button that was clicked.
2158  *
2159  *  @param fd A pointer to the account filter dialog struct.
2160  */
2161 void
gppat_filter_clear_all_cb(GtkWidget * button,AccountFilterDialog * fd)2162 gppat_filter_clear_all_cb (GtkWidget *button,
2163                            AccountFilterDialog *fd)
2164 {
2165     g_return_if_fail(GTK_IS_BUTTON(button));
2166 
2167     ENTER("button %p", button);
2168     fd->visible_types = 0;
2169     gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
2170     gnc_tree_view_account_refilter(fd->tree_view);
2171     LEAVE("types 0x%x", fd->visible_types);
2172 }
2173 
2174 /** The "select all account types" button in the Filter dialog was
2175  *  clicked.  Make all account types visible, and update the page.
2176  *
2177  *  @param button The button that was clicked.
2178  *
2179  *  @param fd A pointer to the account filter dialog struct. */
2180 void
gppat_filter_select_all_cb(GtkWidget * button,AccountFilterDialog * fd)2181 gppat_filter_select_all_cb (GtkWidget *button,
2182                             AccountFilterDialog *fd)
2183 {
2184     g_return_if_fail(GTK_IS_BUTTON(button));
2185 
2186     ENTER("button %p", button);
2187     fd->visible_types = -1;
2188     gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
2189     gnc_tree_view_account_refilter(fd->tree_view);
2190     LEAVE("types 0x%x", fd->visible_types);
2191 }
2192 
2193 /** The "select default account types" button in the Filter dialog was
2194  *  clicked.  Set all account types to their default visibility (which
2195  *  happens to be visible for all of them), and update the page.
2196  *
2197  *  @param button The button that was clicked.
2198  *
2199  *  @param fd A pointer to the account filter dialog struct.*/
2200 void
gppat_filter_select_default_cb(GtkWidget * button,AccountFilterDialog * fd)2201 gppat_filter_select_default_cb (GtkWidget *button,
2202                                 AccountFilterDialog *fd)
2203 {
2204     ENTER("button %p", button);
2205     gppat_filter_select_all_cb(button, fd);
2206     LEAVE(" ");
2207 }
2208 
2209 /** Set the renderer's properties.
2210  *
2211  *  @param column A GtkTreeColumn
2212  *
2213  *  @param renderer The GtkCellRendererToggle being rendered by @column
2214  *
2215  *  @param model The GtkTreeModel being rendered
2216  *
2217  *  @param iter A GtkTreeIter of the current row rendered
2218  *
2219  *  @param data A pointer to the account filter dialog struct. */
2220 static void
gppat_filter_visible_set_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)2221 gppat_filter_visible_set_func (GtkTreeViewColumn *column,
2222                                GtkCellRenderer *renderer,
2223                                GtkTreeModel *model,
2224                                GtkTreeIter *iter,
2225                                gpointer data)
2226 {
2227     AccountFilterDialog *fd = data;
2228     GNCAccountType type;
2229     gboolean active;
2230 
2231     gtk_tree_model_get(model, iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
2232 
2233     active = (fd->visible_types & (1 << type)) ? TRUE : FALSE;
2234     g_object_set (G_OBJECT (renderer), "active", active, NULL);
2235 }
2236 
2237 /** A check box in the tree view was toggled.
2238  *
2239  *  @param renderer The GtkCellRendererToggle being toggled.
2240  *
2241  *  @param fd A pointer to the account filter dialog struct. */
2242 static void
gppat_filter_visible_toggled_cb(GtkCellRendererToggle * renderer,gchar * path_str,AccountFilterDialog * fd)2243 gppat_filter_visible_toggled_cb (GtkCellRendererToggle *renderer,
2244                                  gchar *path_str,
2245                                  AccountFilterDialog *fd)
2246 {
2247     GtkTreeModel *model = fd->model;
2248     GtkTreeIter iter;
2249     GtkTreePath *path;
2250     GNCAccountType type;
2251 
2252     ENTER("toggled %p", path_str);
2253     path = gtk_tree_path_new_from_string(path_str);
2254 
2255     if (gtk_tree_model_get_iter(model, &iter, path))
2256     {
2257         gtk_tree_model_get(model, &iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
2258         fd->visible_types ^= (1 << type);
2259         gnc_tree_view_account_refilter(fd->tree_view);
2260     }
2261     gtk_tree_path_free(path);
2262     LEAVE("types 0x%x", fd->visible_types);
2263 }
2264 
2265 /** The Filter dialog was closed.  Check to see if this was done via
2266  *  the OK button.  If so, make the changes permanent.  If not, revert
2267  *  any changes.
2268  *
2269  *  @param dialog A pointer to the "Filter By" dialog.
2270  *
2271  *  @param response The response code from closing the dialog.
2272  *
2273  *  @param fd A pointer to the account filter dialog struct.
2274  */
2275 void
gppat_filter_response_cb(GtkWidget * dialog,gint response,AccountFilterDialog * fd)2276 gppat_filter_response_cb (GtkWidget *dialog,
2277                           gint       response,
2278                           AccountFilterDialog *fd)
2279 {
2280     gpointer gptemp;
2281 
2282     g_return_if_fail(GTK_IS_DIALOG(dialog));
2283 
2284     ENTER("dialog %p, response %d", dialog, response);
2285 
2286     if (response != GTK_RESPONSE_OK)
2287     {
2288         fd->visible_types = fd->original_visible_types;
2289         fd->show_hidden = fd->original_show_hidden;
2290         fd->show_zero_total = fd->original_show_zero_total;
2291         fd->show_unused = fd->original_show_unused;
2292         gnc_tree_view_account_refilter(fd->tree_view);
2293     }
2294 
2295     /* Clean up and delete dialog */
2296     gptemp = (gpointer)fd->dialog;
2297     g_atomic_pointer_compare_and_exchange(&gptemp,
2298                                           (gpointer)dialog, NULL);
2299     fd->dialog = gptemp;
2300     gtk_widget_destroy(dialog);
2301     LEAVE("types 0x%x", fd->visible_types);
2302 }
2303 
2304 void
account_filter_dialog_create(AccountFilterDialog * fd,GncPluginPage * page)2305 account_filter_dialog_create(AccountFilterDialog *fd, GncPluginPage *page)
2306 {
2307     GtkWidget *dialog, *button;
2308     GtkTreeView *view;
2309     GtkCellRenderer *renderer;
2310     GtkBuilder *builder;
2311     gchar *title;
2312 
2313     ENTER("(fd %p, page %p)", fd, page);
2314 
2315     if (fd->dialog)
2316     {
2317         gtk_window_present(GTK_WINDOW(fd->dialog));
2318         LEAVE("existing dialog");
2319         return;
2320     }
2321 
2322     /* Create the dialog */
2323     builder = gtk_builder_new();
2324     gnc_builder_add_from_file (builder, "dialog-account.glade", "account_filter_by_dialog");
2325     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_filter_by_dialog"));
2326     fd->dialog = dialog;
2327     gtk_window_set_transient_for(GTK_WINDOW(dialog),
2328                                  GTK_WINDOW(GNC_PLUGIN_PAGE(page)->window));
2329     /* Translators: The %s is the name of the plugin page */
2330     title = g_strdup_printf(_("Filter %s by..."),
2331                             gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(page)));
2332     gtk_window_set_title(GTK_WINDOW(dialog), title);
2333     g_free(title);
2334 
2335     /* Remember current state */
2336     fd->original_visible_types = fd->visible_types;
2337     fd->original_show_hidden = fd->show_hidden;
2338     fd->original_show_zero_total = fd->show_zero_total;
2339     fd->original_show_unused = fd->show_unused;
2340 
2341     /* Update the dialog widgets for the current state */
2342     button = GTK_WIDGET(gtk_builder_get_object (builder, "show_hidden"));
2343     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2344                                  fd->show_hidden);
2345     button = GTK_WIDGET(gtk_builder_get_object (builder, "show_zero"));
2346     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2347                                  fd->show_zero_total);
2348     button = GTK_WIDGET(gtk_builder_get_object (builder, "show_unused"));
2349     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2350                                  fd->show_unused);
2351 
2352     /* Set up the tree view and model */
2353     view = GTK_TREE_VIEW(gtk_builder_get_object (builder, FILTER_TREE_VIEW));
2354 
2355     fd->model = gnc_tree_model_account_types_filter_using_mask
2356                 (~(1 << ACCT_TYPE_ROOT));
2357     gtk_tree_view_set_model(view, fd->model);
2358     g_object_unref (fd->model);
2359 
2360     renderer = gtk_cell_renderer_toggle_new();
2361 
2362     g_signal_connect(renderer, "toggled",
2363                      G_CALLBACK(gppat_filter_visible_toggled_cb), fd);
2364 
2365     gtk_tree_view_insert_column_with_data_func (view, -1, NULL, renderer,
2366                      gppat_filter_visible_set_func, fd, NULL);
2367 
2368     gtk_tree_view_insert_column_with_attributes (view,
2369             -1, _("Account Types"), gtk_cell_renderer_text_new(),
2370             "text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME, NULL);
2371 
2372     /* Wire up the rest of the callbacks */
2373     gtk_builder_connect_signals(builder, fd);
2374     g_object_unref(G_OBJECT(builder));
2375 
2376     /* Show it */
2377     gtk_widget_show_all(dialog);
2378     LEAVE(" ");
2379 }
2380 
2381 // page state section
2382 #define ACCT_COUNT    "NumberOfOpenAccounts"
2383 #define ACCT_OPEN     "OpenAccount%d"
2384 #define ACCT_SELECTED "SelectedAccount"
2385 #define SHOW_HIDDEN   "ShowHidden"
2386 #define SHOW_ZERO     "ShowZeroTotal"
2387 #define SHOW_UNUSED   "ShowUnused"
2388 #define ACCT_TYPES    "AccountTypes"
2389 
2390 // account/budget state section
2391 // versions less than 3.2 would crash if key did not have an "_"
2392 #define SHOW_HIDDEN_ACCOUNTS   "Show_Hidden"
2393 #define SHOW_ZERO_TOTALS       "Show_ZeroTotal"
2394 #define SHOW_UNUSED_ACCOUNTS   "Show_Unused"
2395 #define ACCOUNT_TYPES          "Account_Types"
2396 
2397 
2398 typedef struct foo
2399 {
2400     GKeyFile *key_file;
2401     const gchar *group_name;
2402     int count;
2403 } bar_t;
2404 
2405 /** Save information about an expanded row.  This function is called
2406  *  via a gtk_tree_view_map_expanded_rows, which calls it once per
2407  *  expanded row.  Its job is to write the full account name of the
2408  *  row out to the state file.
2409  *
2410  *  @param view A pointer to the GncTreeViewAccount
2411  *
2412  *  @param path A pointer to a particular entry in the tree.
2413  *
2414  *  @param data A pointer to a data structure holding the information
2415  *  related to the state file. */
2416 static void
tree_save_expanded_row(GncTreeViewAccount * view,GtkTreePath * path,gpointer user_data)2417 tree_save_expanded_row (GncTreeViewAccount *view,
2418                         GtkTreePath *path,
2419                         gpointer user_data)
2420 {
2421     Account *account;
2422     bar_t *bar = user_data;
2423     gchar *key;
2424     gchar *account_name;
2425 
2426     account = gnc_tree_view_account_get_account_from_path (view, path);
2427     if (account == NULL)
2428         return;
2429 
2430     account_name = gnc_account_get_full_name(account);
2431     if (account_name == NULL)
2432         return;
2433 
2434     key = g_strdup_printf(ACCT_OPEN, ++bar->count);
2435     g_key_file_set_string(bar->key_file, bar->group_name, key, account_name);
2436     g_free(key);
2437     g_free(account_name);
2438 }
2439 
2440 
2441 /** Save information about the selected row.  Its job is to write the
2442  *  full account name of the row out to the state file.
2443  *
2444  *  @param view A pointer to the GtkTreeView embedded in an
2445  *  account tree page.
2446  *
2447  *  @param path A pointer to a particular entry in the tree.
2448  *
2449  *  @param data A pointer to a data structure holding the information
2450  *  related to the state file. */
2451 static void
tree_save_selected_row(GncTreeViewAccount * view,gpointer user_data)2452 tree_save_selected_row (GncTreeViewAccount *view,
2453                         gpointer user_data)
2454 {
2455     Account *account;
2456     bar_t *bar = user_data;
2457     gchar *account_name;
2458 
2459     account = gnc_tree_view_account_get_selected_account(view);
2460     if (account == NULL)
2461         return;
2462 
2463     account_name = gnc_account_get_full_name (account);
2464     if (account_name == NULL)
2465         return;
2466 
2467     g_key_file_set_string(bar->key_file, bar->group_name, ACCT_SELECTED,
2468                           account_name);
2469     g_free(account_name);
2470 }
2471 
2472 void
gnc_tree_view_account_save(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2473 gnc_tree_view_account_save(GncTreeViewAccount *view,
2474                            AccountFilterDialog *fd,
2475                            GKeyFile *key_file, const gchar *group_name)
2476 {
2477     bar_t bar;
2478 
2479     g_return_if_fail (key_file != NULL);
2480     g_return_if_fail (group_name != NULL);
2481 
2482     ENTER("view %p, key_file %p, group_name %s", view, key_file,
2483           group_name);
2484 
2485     g_key_file_set_integer(key_file, group_name, ACCT_TYPES,
2486                            fd->visible_types);
2487     g_key_file_set_boolean(key_file, group_name, SHOW_HIDDEN,
2488                            fd->show_hidden);
2489     g_key_file_set_boolean(key_file, group_name, SHOW_ZERO,
2490                            fd->show_zero_total);
2491     g_key_file_set_boolean(key_file, group_name, SHOW_UNUSED,
2492                            fd->show_unused);
2493 
2494     bar.key_file = key_file;
2495     bar.group_name = group_name;
2496     bar.count = 0;
2497     tree_save_selected_row(view, &bar);
2498     gtk_tree_view_map_expanded_rows(
2499         GTK_TREE_VIEW(view), (GtkTreeViewMappingFunc) tree_save_expanded_row,
2500         &bar);
2501     g_key_file_set_integer(key_file, group_name, ACCT_COUNT, bar.count);
2502     LEAVE(" ");
2503 
2504 }
2505 
2506 void
gnc_tree_view_account_save_filter(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2507 gnc_tree_view_account_save_filter (GncTreeViewAccount *view,
2508                                    AccountFilterDialog *fd,
2509                                    GKeyFile *key_file,
2510                                    const gchar *group_name)
2511 {
2512     g_return_if_fail (key_file != NULL);
2513     g_return_if_fail (group_name != NULL);
2514 
2515     ENTER("view %p, key_file %p, group_name %s", view, key_file,
2516           group_name);
2517 
2518     g_key_file_set_integer (key_file, group_name, ACCOUNT_TYPES,
2519                            fd->visible_types);
2520     g_key_file_set_boolean (key_file, group_name, SHOW_HIDDEN_ACCOUNTS,
2521                            fd->show_hidden);
2522     g_key_file_set_boolean (key_file, group_name, SHOW_ZERO_TOTALS,
2523                            fd->show_zero_total);
2524     g_key_file_set_boolean (key_file, group_name, SHOW_UNUSED_ACCOUNTS,
2525                            fd->show_unused);
2526     LEAVE("");
2527 }
2528 
2529 /** Expand a row in the tree that was expanded when the user last quit
2530  *  gnucash.  Its job is to map from account name to tree row and
2531  *  expand the row.
2532  *
2533  *  @param view A pointer to the GncTreeViewAccount.
2534  *
2535  *  @param account_name A pointer to the full account name. */
2536 static void
tree_restore_expanded_row(GncTreeViewAccount * view,const gchar * account_name)2537 tree_restore_expanded_row (GncTreeViewAccount *view,
2538                            const gchar *account_name)
2539 {
2540     Account *account;
2541     QofBook *book;
2542 
2543     book = qof_session_get_book(gnc_get_current_session());
2544     g_return_if_fail(book);
2545     account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
2546               account_name);
2547     if (account)
2548         gnc_tree_view_account_expand_to_account(view, account);
2549 }
2550 
2551 
2552 /** Select the row in the tree that was selected when the user last
2553  *  quit gnucash.  Its job is to map from account name to tree row and
2554  *  select the row.
2555  *
2556  *  @param tree A pointer to the GncTreeViewAccount embedded.
2557  *
2558  *  @param account_name A pointer to the full account name. */
2559 static void
tree_restore_selected_row(GncTreeViewAccount * view,const gchar * account_name)2560 tree_restore_selected_row (GncTreeViewAccount *view,
2561                            const gchar *account_name)
2562 {
2563     Account *account;
2564     QofBook *book;
2565 
2566     book = qof_session_get_book(gnc_get_current_session());
2567     g_return_if_fail(book);
2568     account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
2569               account_name);
2570     if (account)
2571         gnc_tree_view_account_set_selected_account(view, account);
2572 }
2573 
2574 void
gnc_tree_view_account_restore(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2575 gnc_tree_view_account_restore(GncTreeViewAccount *view,
2576                               AccountFilterDialog *fd,
2577                               GKeyFile *key_file, const gchar *group_name)
2578 {
2579     GError *error = NULL;
2580     gchar *key, *value;
2581     gint i, count;
2582     gboolean show;
2583 
2584     /* Filter information. Ignore missing keys. */
2585     show = g_key_file_get_boolean(key_file, group_name, SHOW_HIDDEN, &error);
2586     if (error)
2587     {
2588         g_warning("error reading group %s key %s: %s",
2589                   group_name, SHOW_HIDDEN, error->message);
2590         g_error_free(error);
2591         error = NULL;
2592         show = TRUE;
2593     }
2594     fd->show_hidden = show;
2595 
2596     show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO, &error);
2597     if (error)
2598     {
2599         g_warning("error reading group %s key %s: %s",
2600                   group_name, SHOW_ZERO, error->message);
2601         g_error_free(error);
2602         error = NULL;
2603         show = TRUE;
2604     }
2605     fd->show_zero_total = show;
2606 
2607     show = g_key_file_get_boolean(key_file, group_name, SHOW_UNUSED, &error);
2608     if (error)
2609     {
2610         g_warning("error reading group %s key %s: %s",
2611                   group_name, SHOW_UNUSED, error->message);
2612         g_error_free(error);
2613         error = NULL;
2614         show = TRUE;
2615     }
2616     fd->show_unused = show;
2617 
2618     i = g_key_file_get_integer(key_file, group_name, ACCT_TYPES, &error);
2619     if (error)
2620     {
2621         g_warning("error reading group %s key %s: %s",
2622                   group_name, ACCT_TYPES, error->message);
2623         g_error_free(error);
2624         error = NULL;
2625         i = -1;
2626     }
2627     fd->visible_types = i;
2628 
2629     /* Expanded accounts. Skip if count key missing. */
2630     count = g_key_file_get_integer(key_file, group_name, ACCT_COUNT, &error);
2631     if (error == NULL)
2632     {
2633         for (i = 1; i <= count; i++)
2634         {
2635             key = g_strdup_printf(ACCT_OPEN, i);
2636             value = g_key_file_get_string(key_file, group_name, key, &error);
2637             if (error)
2638             {
2639                 g_warning("error reading group %s key %s: %s",
2640                           group_name, key, error->message);
2641                 g_error_free(error);
2642                 error = NULL;
2643             }
2644             else
2645             {
2646                 tree_restore_expanded_row(view, value);
2647                 g_free(value);
2648             }
2649             g_free(key);
2650         }
2651     }
2652     else
2653     {
2654         g_warning("error reading group %s key %s: %s",
2655                   group_name, ACCT_COUNT, error->message);
2656         g_error_free(error);
2657     }
2658 
2659     /* Selected account (if any) */
2660     value = g_key_file_get_string(key_file, group_name, ACCT_SELECTED, NULL);
2661     if (value)
2662     {
2663         tree_restore_selected_row(view, value);
2664         g_free(value);
2665     }
2666 
2667     /* Update tree view for any changes */
2668     gnc_tree_view_account_refilter(view);
2669 }
2670 
2671 void
gnc_tree_view_account_restore_filter(GncTreeViewAccount * view,AccountFilterDialog * fd,GKeyFile * key_file,const gchar * group_name)2672 gnc_tree_view_account_restore_filter (GncTreeViewAccount *view,
2673                                       AccountFilterDialog *fd,
2674                                       GKeyFile *key_file,
2675                                       const gchar *group_name)
2676 {
2677     GError *error = NULL;
2678     gint i;
2679     gboolean show;
2680 
2681     g_return_if_fail (key_file != NULL);
2682     g_return_if_fail (group_name != NULL);
2683 
2684     /* if entry not found, filter will use the default setting */
2685 
2686     /* Filter information. Ignore missing keys. */
2687     show = g_key_file_get_boolean (key_file, group_name, SHOW_HIDDEN_ACCOUNTS, &error);
2688     if (error)
2689     {
2690         g_error_free (error);
2691         error = NULL;
2692     }
2693     else
2694         fd->show_hidden = show;
2695 
2696     show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO_TOTALS, &error);
2697     if (error)
2698     {
2699         g_error_free (error);
2700         error = NULL;
2701     }
2702     else
2703         fd->show_zero_total = show;
2704 
2705     show = g_key_file_get_boolean(key_file, group_name, SHOW_UNUSED_ACCOUNTS, &error);
2706     if (error)
2707     {
2708         g_error_free (error);
2709         error = NULL;
2710     }
2711     else
2712         fd->show_unused = show;
2713 
2714     i = g_key_file_get_integer(key_file, group_name, ACCOUNT_TYPES, &error);
2715     if (error)
2716     {
2717         g_error_free (error);
2718         error = NULL;
2719     }
2720     else
2721         fd->visible_types = i;
2722 }
2723 
2724 // @@fixme -- factor this app-not-gui-specific-logic out.
2725 void
gnc_tree_view_account_name_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_name)2726 gnc_tree_view_account_name_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_name)
2727 {
2728     // check for accounts with the same name among our parent's children.
2729     // should probably factor this consistency check out to the account
2730     // itself....
2731     {
2732         Account *parent = gnc_account_get_parent(account);
2733         Account *existing = gnc_account_lookup_by_name(parent, new_name);
2734         if (existing != NULL && existing != account)
2735         {
2736             PERR("account with the same name [%s] already exists.", new_name);
2737             return;
2738         }
2739     }
2740     xaccAccountSetName(account, new_name);
2741 }
2742 
2743 void
gnc_tree_view_account_code_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_code)2744 gnc_tree_view_account_code_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_code)
2745 {
2746     if (g_strcmp0(xaccAccountGetCode(account), new_code) == 0)
2747         return;
2748     xaccAccountSetCode(account, new_code);
2749 }
2750 
2751 void
gnc_tree_view_account_description_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_desc)2752 gnc_tree_view_account_description_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_desc)
2753 {
2754     if (g_strcmp0(xaccAccountGetDescription(account), new_desc) == 0)
2755         return;
2756     xaccAccountSetDescription(account, new_desc);
2757 }
2758 
2759 void
gnc_tree_view_account_notes_edited_cb(Account * account,GtkTreeViewColumn * col,const gchar * new_notes)2760 gnc_tree_view_account_notes_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_notes)
2761 {
2762     if (g_strcmp0(xaccAccountGetNotes(account), new_notes) == 0)
2763         return;
2764     xaccAccountSetNotes(account, new_notes);
2765 }
2766 
2767 static void
gtva_set_column_editor(GncTreeViewAccount * view,GtkTreeViewColumn * column,GncTreeViewAccountColumnTextEdited edited_cb)2768 gtva_set_column_editor(GncTreeViewAccount *view,
2769                        GtkTreeViewColumn *column,
2770                        GncTreeViewAccountColumnTextEdited edited_cb)
2771 {
2772     GList *renderers_orig, *renderers;
2773     GtkCellRenderer *renderer = NULL;
2774 
2775     // look for the first text-renderer; on the 0th column of the account tree,
2776     // there are two renderers: pixbuf and text.  So find the text one.
2777     for (renderers_orig = renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
2778             renderers && !GTK_IS_CELL_RENDERER_TEXT(renderers->data);
2779             renderers = renderers->next);
2780     if (renderers)
2781         renderer = GTK_CELL_RENDERER(renderers->data);
2782     g_list_free(renderers_orig);
2783     g_return_if_fail(renderer != NULL);
2784     gtva_setup_column_renderer_edited_cb(GNC_TREE_VIEW_ACCOUNT(view), column, renderer, edited_cb);
2785 }
2786 
2787 void
gnc_tree_view_account_set_name_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2788 gnc_tree_view_account_set_name_edited(GncTreeViewAccount *view,
2789                                       GncTreeViewAccountColumnTextEdited edited_cb)
2790 {
2791     GncTreeViewAccountPrivate *priv;
2792     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2793     gtva_set_column_editor(view, priv->name_column, edited_cb);
2794 }
2795 
2796 void
gnc_tree_view_account_set_code_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2797 gnc_tree_view_account_set_code_edited(GncTreeViewAccount *view,
2798                                       GncTreeViewAccountColumnTextEdited edited_cb)
2799 {
2800     GncTreeViewAccountPrivate *priv;
2801     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2802     gtva_set_column_editor(view, priv->code_column, edited_cb);
2803 }
2804 
2805 void
gnc_tree_view_account_set_description_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2806 gnc_tree_view_account_set_description_edited(GncTreeViewAccount *view,
2807         GncTreeViewAccountColumnTextEdited edited_cb)
2808 {
2809     GncTreeViewAccountPrivate *priv;
2810     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2811     gtva_set_column_editor(view, priv->desc_column, edited_cb);
2812 }
2813 
2814 void
gnc_tree_view_account_set_notes_edited(GncTreeViewAccount * view,GncTreeViewAccountColumnTextEdited edited_cb)2815 gnc_tree_view_account_set_notes_edited(GncTreeViewAccount *view,
2816                                        GncTreeViewAccountColumnTextEdited edited_cb)
2817 {
2818     GncTreeViewAccountPrivate *priv;
2819     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2820     gtva_set_column_editor(view, priv->notes_column, edited_cb);
2821 }
2822 
2823 static
gnc_tree_view_search_compare(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)2824 gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
2825         const gchar *key, GtkTreeIter *iter, gpointer search_data)
2826 {
2827     gchar *normalized_key;
2828     gchar *case_normalized_key = NULL;
2829     gboolean match = FALSE;
2830 
2831     normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
2832     if (normalized_key)
2833         case_normalized_key = g_utf8_casefold (normalized_key, -1);
2834     if (case_normalized_key)
2835     {
2836         int i;
2837 
2838         for (i=0;i<3;i++)
2839         {
2840             gchar *normalized_string;
2841             gchar *case_normalized_string = NULL;
2842             gchar *str = NULL;
2843 
2844             switch (i)
2845             {
2846                 case 0:
2847                     gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_NAME,&str,-1);
2848                     break;
2849                 case 1:
2850                     gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_CODE,&str,-1);
2851                     break;
2852                 case 2:
2853                     gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,&str,-1);
2854                     break;
2855             }
2856 
2857             if (!str)
2858                 continue;
2859 
2860             normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
2861             if (normalized_string)
2862                 case_normalized_string = g_utf8_casefold (normalized_string, -1);
2863             if (case_normalized_string&&NULL!=strstr(case_normalized_string,case_normalized_key))
2864                 match=TRUE;
2865 
2866             g_free (str);
2867             g_free (normalized_string);
2868             g_free (case_normalized_string);
2869 
2870             if (match)
2871                 break;
2872         }
2873     }
2874 
2875     g_free (normalized_key);
2876     g_free (case_normalized_key);
2877 
2878     // inverted return (FALSE means a match)
2879     return !match;
2880 }
2881 
gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount * view,GFunc editing_started_cb,gpointer editing_cb_data)2882 void gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount *view,
2883     GFunc editing_started_cb, gpointer editing_cb_data)
2884 {
2885     gnc_tree_view_set_editing_started_cb (GNC_TREE_VIEW(view),
2886             editing_started_cb, editing_cb_data);
2887 }
2888 
gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount * view,GFunc editing_finished_cb,gpointer editing_cb_data)2889 void gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount *view,
2890     GFunc editing_finished_cb, gpointer editing_cb_data)
2891 {
2892     gnc_tree_view_set_editing_finished_cb (GNC_TREE_VIEW(view),
2893             editing_finished_cb, editing_cb_data);
2894 }
2895