GnuCash  4.14+
gnc-tree-view-account.c
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"
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 
56 /* This static indicates the debugging module that this .o belongs to. */
57 static QofLogModule log_module = GNC_MOD_GUI;
58 
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 
95 {
96  AccountViewInfo avi;
97 
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 
116 
117 #define GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(o) \
118  ((GncTreeViewAccountPrivate*)gnc_tree_view_account_get_instance_private((GncTreeViewAccount*)o))
119 
120 
121 /************************************************************/
122 /* g_object required functions */
123 /************************************************************/
124 
125 static GObjectClass *parent_class = NULL;
126 
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
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
162 gnc_tree_view_account_init (GncTreeViewAccount *view)
163 {
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
177 gnc_tree_view_account_finalize (GObject *object)
178 {
179  GncTreeViewAccount *account_view;
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
665 acc_color_data_func (GtkTreeViewColumn *col,
666  GtkCellRenderer *renderer,
667  GtkTreeModel *model,
668  GtkTreeIter *iter,
669  gpointer view)
670 {
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 
720 static void
721 gnc_tree_view_account_color_update (gpointer gsettings, gchar *key, gpointer user_data)
722 {
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
734 }
735 
739 void
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 *
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;
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 
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. */
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 *
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 *
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
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));
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
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
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
1193  AccountViewInfo *avi)
1194 {
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
1221  AccountViewInfo *avi)
1222 {
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 
1240  account_view, gnc_tree_view_account_filter_by_view_info,
1241  &priv->avi, NULL);
1242 
1243  LEAVE(" ");
1244 }
1245 
1246 static gboolean
1247 gnc_tree_view_account_filter_helper (GtkTreeModel *model,
1248  GtkTreeIter *iter,
1249  gpointer data)
1250 {
1251  Account *account;
1252  GncTreeViewAccount *view = data;
1254 
1255  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), FALSE);
1256  g_return_val_if_fail (iter != NULL, FALSE);
1257 
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
1277  gpointer data,
1278  GSourceFunc destroy)
1279 {
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 
1297  LEAVE(" ");
1298 }
1299 
1300 /*
1301  * Forces the entire account tree to be re-evaluated for visibility.
1302  */
1303 void
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
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 *
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 *
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));
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 *
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
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 
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;
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
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 *
1574 {
1575  GtkTreeSelection *selection;
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
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
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
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 *
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
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
1810 gtva_update_column_names (GncTreeView *view)
1811 {
1813  const gchar *mnemonic;
1814 
1815  priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
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);
1838 }
1839 
1840 
1841 static 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
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 *
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 
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 
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 
1953 void
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 *
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 *
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 
2043 gboolean
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 
2103 void
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 
2122 void
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 
2141 void
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 
2161 void
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 
2180 void
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 
2200 void
2202  AccountFilterDialog *fd)
2203 {
2204  ENTER("button %p", button);
2205  gppat_filter_select_all_cb(button, fd);
2206  LEAVE(" ");
2207 }
2208 
2220 static void
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 
2242 static void
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 
2275 void
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
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 
2416 static void
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 
2451 static void
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 
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
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
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 
2536 static void
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)
2549 }
2550 
2551 
2559 static void
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)
2572 }
2573 
2574 void
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 */
2669 }
2670 
2671 void
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
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
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
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
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
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
2788 gnc_tree_view_account_set_name_edited(GncTreeViewAccount *view,
2789  GncTreeViewAccountColumnTextEdited edited_cb)
2790 {
2792  priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2793  gtva_set_column_editor(view, priv->name_column, edited_cb);
2794 }
2795 
2796 void
2797 gnc_tree_view_account_set_code_edited(GncTreeViewAccount *view,
2798  GncTreeViewAccountColumnTextEdited edited_cb)
2799 {
2801  priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2802  gtva_set_column_editor(view, priv->code_column, edited_cb);
2803 }
2804 
2805 void
2806 gnc_tree_view_account_set_description_edited(GncTreeViewAccount *view,
2807  GncTreeViewAccountColumnTextEdited edited_cb)
2808 {
2810  priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2811  gtva_set_column_editor(view, priv->desc_column, edited_cb);
2812 }
2813 
2814 void
2815 gnc_tree_view_account_set_notes_edited(GncTreeViewAccount *view,
2816  GncTreeViewAccountColumnTextEdited edited_cb)
2817 {
2819  priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
2820  gtva_set_column_editor(view, priv->notes_column, edited_cb);
2821 }
2822 
2823 static
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_NFC);
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_NFC);
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 
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 
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 }
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2905
void gnc_tree_view_account_get_view_info(GncTreeViewAccount *account_view, AccountViewInfo *avi)
Given pointers to an account tree and old style filter block, this function will copy the current con...
GtkTreeViewColumn * gnc_tree_view_account_add_property_column(GncTreeViewAccount *view, const gchar *column_title, const gchar *propname)
Add a new column to the set of columns in an account tree view.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
The instance data structure for a content plugin.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
GList * gnc_tree_view_account_get_selected_accounts(GncTreeViewAccount *view)
This function returns a list of the accounts associated with the selected items in the account tree v...
Account * gnc_tree_view_account_get_cursor_account(GncTreeViewAccount *view)
This function returns the account in the account tree view at the current location of the cursor...
void xaccAccountSetNotes(Account *acc, const char *str)
Set the account&#39;s notes.
Definition: Account.cpp:2628
void gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount *view, GFunc editing_finished_cb, gpointer editing_cb_data)
Setup the callback for when the user finishes editing the account tree so actions can be enabled like...
int safe_utf8_collate(const char *da, const char *db)
Collate two UTF-8 strings.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3285
void gnc_tree_view_set_editing_started_cb(GncTreeView *view, GFunc editing_started_cb, gpointer editing_cb_data)
Setup a callback for when the user starts editing so appropriate actions can be taken like disable th...
void gppat_filter_response_cb(GtkWidget *dialog, gint response, AccountFilterDialog *fd)
The Filter dialog was closed.
const char * xaccAccountGetCode(const Account *acc)
Get the account&#39;s accounting code.
Definition: Account.cpp:3362
common utilities for manipulating a GtkTreeView within gnucash
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Retrieve the name of this page.
Account * gnc_tree_model_account_get_account(GncTreeModelAccount *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash account.
void xaccAccountSetCode(Account *acc, const char *str)
Set the account&#39;s accounting code.
Definition: Account.cpp:2473
void gppat_filter_select_all_cb(GtkWidget *button, AccountFilterDialog *fd)
The "select all account types" button in the Filter dialog was clicked.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
stop here; the following types just aren&#39;t ready for prime time
Definition: Account.h:164
void gnc_tree_view_account_expand_to_account(GncTreeViewAccount *view, Account *account)
This function forces the account tree expand whatever levels are necessary to make the specified acco...
void gnc_tree_view_account_column_add_color(GncTreeViewAccount *view, GtkTreeViewColumn *col)
Add the account color background data function to the GncTreeViewAccount column to show or not the co...
void gnc_tree_view_account_set_view_info(GncTreeViewAccount *account_view, AccountViewInfo *avi)
Given pointers to an account tree and old style filter block, this function will applies the settings...
void gnc_tree_view_set_show_column_menu(GncTreeView *view, gboolean visible)
This function is called to set the "show-column-menu" property on this view.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
void gppat_filter_clear_all_cb(GtkWidget *button, AccountFilterDialog *fd)
The "clear all account types" button in the Filter dialog was clicked.
void gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount *view, GFunc editing_started_cb, gpointer editing_cb_data)
Setup the callback for when the user starts editing the account tree so actions can be disabled like ...
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
GtkTreeViewColumn * gnc_tree_view_add_numeric_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *sizing_text, gint model_data_column, gint model_color_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new numeric column to a GncTreeView base view.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
gboolean gnc_tree_model_account_get_iter_from_account(GncTreeModelAccount *model, Account *account, GtkTreeIter *iter)
Convert a model/account pair into a gtk_tree_model_iter.
gnc_commodity * gnc_default_report_currency(void)
Return the default currency for use in reports, as set by the user.
Definition: gnc-ui-util.c:1237
void gnc_tree_view_account_set_selected_accounts(GncTreeViewAccount *view, GList *account_list, gboolean show_last)
This function selects a set of accounts in the account tree view.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Definition: gnc-ui-util.c:1204
void gppat_filter_show_hidden_toggled_cb(GtkToggleButton *button, AccountFilterDialog *fd)
The "show hidden" button in the Filter dialog changed state.
gboolean xaccAccountIsHidden(const Account *acc)
Should this account be "hidden".
Definition: Account.cpp:4369
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
The gnc_account_lookup_by_name() subroutine fetches the account by name from the descendants of the s...
Definition: Account.cpp:3089
int xaccAccountOrder(const Account *aa, const Account *ab)
The xaccAccountOrder() subroutine defines a sorting order on accounts.
Definition: Account.cpp:2368
gint gnc_tree_view_append_column(GncTreeView *view, GtkTreeViewColumn *column)
Add a column to a view based upon a GncTreeView.
GtkTreeModel implementation for gnucash account tree.
GtkTreeViewColumn * gnc_tree_view_add_toggle_column(GncTreeView *view, const gchar *column_title, const gchar *column_short_title, const gchar *pref_name, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn, renderer_toggled toggle_edited_cb)
This function adds a new toggle column to a GncTreeView base view.
void gnc_tree_view_set_editing_finished_cb(GncTreeView *view, GFunc editing_finished_cb, gpointer editing_cb_data)
Setup a callback for when the user finishes editing so appropriate actions can be taken like enable t...
void gnc_tree_view_account_set_filter(GncTreeViewAccount *view, gnc_tree_view_account_filter_func func, gpointer data, GSourceFunc destroy)
This function attaches a filter function to the given account tree.
GtkCellRenderer * gnc_tree_view_column_get_renderer(GtkTreeViewColumn *column)
Return the "main" cell renderer from a GtkTreeViewColumn added to a GncTreeView my one of the conveni...
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:578
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3314
GtkTreeViewColumn * gnc_tree_view_add_text_view_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *icon_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new text view column to a GncTreeView base view.
void gnc_tree_model_account_clear_cache(GncTreeModelAccount *model)
Clear the tree model account cached values.
Account handling public routines.
void gnc_tree_view_account_select_subaccounts(GncTreeViewAccount *view, Account *account)
This function selects all sub-accounts of an account in the account tree view.
void xaccAccountSetPlaceholder(Account *acc, gboolean val)
Set the "placeholder" flag for an account.
Definition: Account.cpp:4274
void gnc_tree_view_account_refilter(GncTreeViewAccount *view)
This function forces the account tree filter to be evaluated.
Gobject helper routines.
GtkTreeView implementation for gnucash account tree.
GtkTreeModel * gnc_tree_model_account_new(Account *root)
Create a new GtkTreeModel for manipulating gnucash accounts.
GtkTreeViewColumn * gnc_tree_view_account_add_custom_column(GncTreeViewAccount *account_view, const gchar *column_title, GncTreeViewAccountColumnSource col_source_cb, GncTreeViewAccountColumnTextEdited col_edited_cb)
Add a new custom column to the set of columns in an account tree view.
GtkTreeView * gnc_tree_view_account_new_with_root(Account *root, gboolean show_root)
Create a new account tree view.
const char * xaccAccountGetDescription(const Account *acc)
Get the account&#39;s description.
Definition: Account.cpp:3369
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting, their "always" visibility setting, and the last saved state if available.
General utilities for dealing with accounting periods.
gboolean gnc_plugin_page_account_tree_filter_accounts(Account *account, gpointer user_data)
This function tells the account tree view whether or not to filter out a particular account...
void gnc_tree_view_account_clear_model_cache(GncTreeViewAccount *view)
This function clears the tree model account cache so the values will be updated/refreshed.
Account * gnc_tree_view_account_get_account_from_iter(GtkTreeModel *s_model, GtkTreeIter *s_iter)
This function returns the account associated with the specified iter.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
Account * gnc_account_lookup_by_full_name(const Account *any_acc, const gchar *name)
The gnc_account_lookup_full_name() subroutine works like gnc_account_lookup_by_name, but uses fully-qualified names using the given separator.
Definition: Account.cpp:3169
Account * gnc_tree_view_account_get_account_from_path(GncTreeViewAccount *view, GtkTreePath *s_path)
This function returns the account associated with the specified path.
All type declarations for the whole Gnucash engine.
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition: Account.cpp:4733
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:105
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition: Account.cpp:4357
GtkTreeModel implementation to display account types in a GtkTreeView.
GLib helper routines.
Generic api to store and retrieve preferences.
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition: Account.cpp:4292
void gnc_tree_view_account_set_selected_account(GncTreeViewAccount *view, Account *account)
This function selects an account in the account tree view.
void gppat_filter_show_unused_toggled_cb(GtkToggleButton *button, AccountFilterDialog *fd)
The "show unused" button in the Filter dialog changed state.
void xaccAccountSetHidden(Account *acc, gboolean val)
Set the "hidden" flag for an account.
Definition: Account.cpp:4363
gint gnc_tree_view_account_count_children(GncTreeViewAccount *view, Account *account)
This function determines if an account in the account tree view has any visible children.
GtkTreePath * gnc_tree_model_account_get_path_from_account(GncTreeModelAccount *model, Account *account)
Convert a model/account pair into a gtk_tree_model_path.
GtkTreeViewColumn * gnc_tree_view_add_text_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *icon_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new text column to a GncTreeView base view.
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition: Account.cpp:4268
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
Account * gnc_tree_view_account_get_selected_account(GncTreeViewAccount *view)
This function returns the account associated with the selected item in the account tree view...
gboolean(* gnc_tree_view_account_filter_func)(Account *account, gpointer data)
This is the description of a filter function used by the account tree.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account&#39;s description.
Definition: Account.cpp:2492
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3307
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition: Account.cpp:4524
void xaccAccountSetName(Account *acc, const char *str)
Set the account&#39;s name.
Definition: Account.cpp:2453
The hidden root account of an account tree.
Definition: Account.h:156
Commodity handling public routines.
The Credit card account is used to denote credit (e.g.
Definition: Account.h:116
void gppat_filter_select_default_cb(GtkWidget *button, AccountFilterDialog *fd)
The "select default account types" button in the Filter dialog was clicked.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143
const char * xaccAccountGetNotes(const Account *acc)
Get the account&#39;s notes.
Definition: Account.cpp:3422
void gppat_filter_show_zero_toggled_cb(GtkToggleButton *button, AccountFilterDialog *fd)
The "show zero totals" button in the Filter dialog changed state.