GnuCash  5.6-150-g038405b370+
gnc-tree-model-account.c
1 /*
2  * gnc-tree-model-account.c -- GtkTreeModel implementation to
3  * display accounts in a GtkTreeView.
4  *
5  * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
6  * Copyright (C) 2003 David Hampton <hampton@employees.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, contact:
20  *
21  * Free Software Foundation Voice: +1-617-542-5942
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
23  * Boston, MA 02110-1301, USA gnu@gnu.org
24  */
25 
26 #include <config.h>
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <string.h>
31 
32 #include "gnc-tree-model-account.h"
33 #include "gnc-component-manager.h"
34 #include "Account.h"
35 #include "dialog-utils.h"
36 #include "gnc-accounting-period.h"
37 #include "gnc-commodity.h"
38 #include "gnc-prefs.h"
39 #include "gnc-engine.h"
40 #include "gnc-event.h"
41 #include "gnc-gobject-utils.h"
42 #include "gnc-ui-balances.h"
43 #include "gnc-ui-util.h"
44 #include <gnc-locale-tax.h>
45 
46 #define TREE_MODEL_ACCOUNT_CM_CLASS "tree-model-account"
47 
49 static QofLogModule log_module = GNC_MOD_GUI;
50 
52 static void gnc_tree_model_account_finalize (GObject *object);
53 static void gnc_tree_model_account_dispose (GObject *object);
54 
56 static void gnc_tree_model_account_tree_model_init (GtkTreeModelIface *iface);
57 static GtkTreeModelFlags gnc_tree_model_account_get_flags (GtkTreeModel *tree_model);
58 static int gnc_tree_model_account_get_n_columns (GtkTreeModel *tree_model);
59 static GType gnc_tree_model_account_get_column_type (GtkTreeModel *tree_model,
60  int index);
61 static gboolean gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
62  GtkTreeIter *iter,
63  GtkTreePath *path);
64 static GtkTreePath *gnc_tree_model_account_get_path (GtkTreeModel *tree_model,
65  GtkTreeIter *iter);
66 static void gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
67  GtkTreeIter *iter,
68  int column,
69  GValue *value);
70 static gboolean gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
71  GtkTreeIter *iter);
72 static gboolean gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
73  GtkTreeIter *iter,
74  GtkTreeIter *parent);
75 static gboolean gnc_tree_model_account_iter_has_child (GtkTreeModel *tree_model,
76  GtkTreeIter *iter);
77 static int gnc_tree_model_account_iter_n_children (GtkTreeModel *tree_model,
78  GtkTreeIter *iter);
79 static gboolean gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
80  GtkTreeIter *iter,
81  GtkTreeIter *parent,
82  int n);
83 static gboolean gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
84  GtkTreeIter *iter,
85  GtkTreeIter *child);
86 
88 static void gnc_tree_model_account_event_handler (QofInstance *entity,
89  QofEventId event_type,
90  GncTreeModelAccount *model,
91  GncEventData *ed);
92 
95 {
96  GncTreeModel gnc_tree_model;
97  int stamp;
99  QofBook *book;
100  Account *root;
101  gint event_handler_id;
102  gchar *negative_color;
103 
104  GHashTable *account_values_hash;
105 
106 };
107 
108 G_DEFINE_TYPE_WITH_CODE (GncTreeModelAccount,
109  gnc_tree_model_account, GNC_TYPE_TREE_MODEL,
110  G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
111  gnc_tree_model_account_tree_model_init))
112 
113 /************************************************************/
114 /* Account Tree Model - Misc Functions */
115 /************************************************************/
116 
117 
118 
124 static void
125 gnc_tree_model_account_update_color (gpointer gsettings, gchar *key, gpointer user_data)
126 {
127  gboolean use_red;
128 
129  g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(user_data));
130  GncTreeModelAccount *model = user_data;
131 
132  // destroy/recreate the cached account value hash to force update
133  g_hash_table_destroy (model->account_values_hash);
134  model->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
135  g_free, g_free);
136 
137  use_red = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
138 
139  if (model->negative_color)
140  g_free (model->negative_color);
141 
142  if (use_red)
143  model->negative_color = gnc_get_negative_color ();
144  else
145  model->negative_color = NULL;
146 }
147 
148 /************************************************************/
149 /* g_object required functions */
150 /************************************************************/
151 
152 static void
153 gnc_tree_model_account_class_init (GncTreeModelAccountClass *klass)
154 {
155  GObjectClass *o_class;
156 
157  o_class = G_OBJECT_CLASS(klass);
158 
159  /* GObject signals */
160  o_class->finalize = gnc_tree_model_account_finalize;
161  o_class->dispose = gnc_tree_model_account_dispose;
162 }
163 
164 static void
165 gnc_tree_model_account_init (GncTreeModelAccount *model)
166 {
167  gboolean use_red;
168 
169  ENTER("model %p", model);
170  while (model->stamp == 0)
171  {
172  model->stamp = g_random_int ();
173  }
174 
175  use_red = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
176 
177  model->book = NULL;
178  model->root = NULL;
179 
180  if (model->negative_color)
181  g_free (model->negative_color);
182 
183  if (use_red)
184  model->negative_color = gnc_get_negative_color ();
185  else
186  model->negative_color = NULL;
187 
188  // create the account values cache hash
189  model->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
190  g_free, g_free);
191 
192  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
193  gnc_tree_model_account_update_color,
194  model);
195 
196  LEAVE(" ");
197 }
198 
199 static void
200 gnc_tree_model_account_finalize (GObject *object)
201 {
202  GncTreeModelAccount *model;
203 
204  g_return_if_fail (object != NULL);
205  g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(object));
206 
207  ENTER("model %p", object);
208 
209  model = GNC_TREE_MODEL_ACCOUNT(object);
210 
211  model->book = NULL;
212 
213  G_OBJECT_CLASS(gnc_tree_model_account_parent_class)->finalize (object);
214  LEAVE(" ");
215 }
216 
217 static void
218 gnc_tree_model_account_dispose (GObject *object)
219 {
220  GncTreeModelAccount *model;
221 
222  g_return_if_fail (object != NULL);
223  g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(object));
224 
225  ENTER("model %p", object);
226 
227  model = GNC_TREE_MODEL_ACCOUNT(object);
228 
229  if (model->event_handler_id)
230  {
231  qof_event_unregister_handler (model->event_handler_id);
232  model->event_handler_id = 0;
233  }
234 
235  if (model->negative_color)
236  g_free (model->negative_color);
237 
238  // destroy the cached account values
239  g_hash_table_destroy (model->account_values_hash);
240 
241  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
242  gnc_tree_model_account_update_color,
243  model);
244 
245  model->root = NULL;
246  G_OBJECT_CLASS(gnc_tree_model_account_parent_class)->dispose (object);
247  LEAVE(" ");
248 }
249 
250 
251 /************************************************************/
252 /* New Model Creation */
253 /************************************************************/
254 
255 GtkTreeModel *
257 {
258  GncTreeModelAccount *model;
259  const GList *item;
260 
261  ENTER("root %p", root);
262  item = gnc_gobject_tracking_get_list (GNC_TREE_MODEL_ACCOUNT_NAME);
263  for ( ; item; item = g_list_next (item))
264  {
265  model = (GncTreeModelAccount *)item->data;
266  if (model->root == root)
267  {
268  g_object_ref (G_OBJECT(model));
269  LEAVE("returning existing model %p", model);
270  return GTK_TREE_MODEL(model);
271  }
272  }
273 
274  model = g_object_new (GNC_TYPE_TREE_MODEL_ACCOUNT, NULL);
275 
276  model->book = gnc_get_current_book();
277  model->root = root;
278 
279  model->event_handler_id = qof_event_register_handler
280  ((QofEventHandler)gnc_tree_model_account_event_handler, model);
281 
282  LEAVE("model %p", model);
283  return GTK_TREE_MODEL(model);
284 }
285 
286 
287 /************************************************************/
288 /* Gnc Tree Model Debugging Utility Function */
289 /************************************************************/
290 
291 #define ITER_STRING_LEN 128
292 
293 static const gchar *
294 iter_to_string (GtkTreeIter *iter)
295 {
296 #ifdef G_THREADS_ENABLED
297  static GPrivate gtmits_buffer_key = G_PRIVATE_INIT(g_free);
298  gchar *string;
299 
300  string = g_private_get (&gtmits_buffer_key);
301  if (string == NULL)
302  {
303  string = g_malloc(ITER_STRING_LEN + 1);
304  g_private_set (&gtmits_buffer_key, string);
305  }
306 #else
307  static char string[ITER_STRING_LEN + 1];
308 #endif
309 
310  if (iter)
311  snprintf (string, ITER_STRING_LEN,
312  "[stamp:%x data:%p (%s), %p, %d]",
313  iter->stamp, iter->user_data,
314  xaccAccountGetName ((Account *) iter->user_data),
315  iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
316  else
317  strcpy (string, "(null)");
318  return string;
319 }
320 
321 
322 /************************************************************/
323 /* Gtk Tree Model Required Interface Functions */
324 /************************************************************/
325 
326 static void
327 gnc_tree_model_account_tree_model_init (GtkTreeModelIface *iface)
328 {
329  iface->get_flags = gnc_tree_model_account_get_flags;
330  iface->get_n_columns = gnc_tree_model_account_get_n_columns;
331  iface->get_column_type = gnc_tree_model_account_get_column_type;
332  iface->get_iter = gnc_tree_model_account_get_iter;
333  iface->get_path = gnc_tree_model_account_get_path;
334  iface->get_value = gnc_tree_model_account_get_value;
335  iface->iter_next = gnc_tree_model_account_iter_next;
336  iface->iter_children = gnc_tree_model_account_iter_children;
337  iface->iter_has_child = gnc_tree_model_account_iter_has_child;
338  iface->iter_n_children = gnc_tree_model_account_iter_n_children;
339  iface->iter_nth_child = gnc_tree_model_account_iter_nth_child;
340  iface->iter_parent = gnc_tree_model_account_iter_parent;
341 }
342 
343 static GtkTreeModelFlags
344 gnc_tree_model_account_get_flags (GtkTreeModel *tree_model)
345 {
346  return 0;
347 }
348 
349 static int
350 gnc_tree_model_account_get_n_columns (GtkTreeModel *tree_model)
351 {
352  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), -1);
353 
354  return GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS;
355 }
356 
357 static GType
358 gnc_tree_model_account_get_column_type (GtkTreeModel *tree_model, int index)
359 {
360  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), G_TYPE_INVALID);
361  g_return_val_if_fail ((index < GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID);
362 
363  switch (index)
364  {
365  case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
366  case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
367  case GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY:
368  case GNC_TREE_MODEL_ACCOUNT_COL_CODE:
369  case GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION:
370  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT:
371  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT:
372  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
373  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
374  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
375  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
376  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT_EXPLANATION:
377  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
378  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
379  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
380  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT:
381  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE:
382  case GNC_TREE_MODEL_ACCOUNT_COL_EARLIEST_DATE:
383  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN:
384  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT:
385  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL:
386  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT:
387  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD:
388  case GNC_TREE_MODEL_ACCOUNT_COL_NOTES:
389  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO:
390  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT:
391  case GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM:
392 
393  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT:
394  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT:
395  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE:
396  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD:
397  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED:
398  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED:
399  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN:
400  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL:
401  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD:
402  return G_TYPE_STRING;
403 
404  case GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN:
405  case GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER:
406  case GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE:
407  return G_TYPE_BOOLEAN;
408 
409  default:
410  g_assert_not_reached ();
411  return G_TYPE_INVALID;
412  }
413 }
414 
415 static gboolean
416 gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
417  GtkTreeIter *iter,
418  GtkTreePath *path)
419 {
420  GncTreeModelAccount *model;
421  Account *account, *parent;
422  gint i, *indices;
423 
424  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
425 
426  {
427  gchar *path_string = gtk_tree_path_to_string (path);
428  ENTER("model %p, iter %p, path %s", tree_model, iter, path_string);
429  g_free (path_string);
430  }
431 
432  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
433 
434  if (gtk_tree_path_get_depth (path) <= 0)
435  {
436  LEAVE("bad depth");
437  return FALSE;
438  }
439 
440  indices = gtk_tree_path_get_indices (path);
441  if (indices[0] != 0)
442  {
443  LEAVE("bad root index");
444  return FALSE;
445  }
446 
447  parent = NULL;
448  account = model->root;
449  for (i = 1; i < gtk_tree_path_get_depth (path); i++)
450  {
451  parent = account;
452  account = gnc_account_nth_child (parent, indices[i]);
453  if (account == NULL)
454  {
455  iter->stamp = 0;
456  LEAVE("bad index");
457  return FALSE;
458  }
459  }
460 
461  iter->stamp = model->stamp;
462  iter->user_data = account;
463  iter->user_data2 = parent;
464  iter->user_data3 = GINT_TO_POINTER(indices[i - 1]);
465 
466  LEAVE("iter %s", iter_to_string (iter));
467  return TRUE;
468 }
469 
470 static GtkTreePath *
471 gnc_tree_model_account_get_path (GtkTreeModel *tree_model,
472  GtkTreeIter *iter)
473 {
474  GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
475  Account *account, *parent;
476  GtkTreePath *path;
477  gint i;
478 
479  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
480  g_return_val_if_fail (iter != NULL, NULL);
481  g_return_val_if_fail (iter->user_data != NULL, NULL);
482  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
483 
484  ENTER("model %p, iter %s", model, iter_to_string (iter));
485 
486  if (model->root == NULL)
487  {
488  LEAVE("failed (1)");
489  return NULL;
490  }
491 
492  account = (Account *) iter->user_data;
493  parent = (Account *) iter->user_data2;
494 
495  path = gtk_tree_path_new ();
496  while (parent)
497  {
498  i = gnc_account_child_index (parent, account);
499  if (i == -1)
500  {
501  gtk_tree_path_free (path);
502  LEAVE("failed (3)");
503  return NULL;
504  }
505  gtk_tree_path_prepend_index (path, i);
506  account = parent;
507  parent = gnc_account_get_parent (account);
508  };
509 
510  /* Add the root node. */
511  gtk_tree_path_prepend_index (path, 0);
512 
513  {
514  gchar *path_string = gtk_tree_path_to_string (path);
515  LEAVE("path (4) %s", path_string);
516  g_free (path_string);
517  }
518  return path;
519 }
520 
521 static void
522 gnc_tree_model_account_set_color (GncTreeModelAccount *model,
523  gboolean negative,
524  GValue *value)
525 {
526  if (negative)
527  g_value_set_static_string (value, model->negative_color);
528  else
529  g_value_set_static_string (value, NULL);
530 }
531 
532 static gchar *
533 gnc_tree_model_account_compute_period_balance (GncTreeModelAccount *model,
534  Account *acct,
535  gboolean recurse,
536  gboolean *negative)
537 {
538  GNCPrintAmountInfo print_info;
539  time64 t1, t2;
540  gnc_numeric b3;
541 
542  if (negative)
543  *negative = FALSE;
544 
545  if (acct == model->root)
546  return g_strdup ("");
547 
548  t1 = gnc_accounting_period_fiscal_start ();
549  t2 = gnc_accounting_period_fiscal_end ();
550 
551  if (t1 > t2)
552  return g_strdup ("");
553 
554  b3 = xaccAccountGetBalanceChangeForPeriod (acct, t1, t2, recurse);
555  if (gnc_reverse_balance (acct))
556  b3 = gnc_numeric_neg (b3);
557 
558  if (negative)
559  *negative = gnc_numeric_negative_p (b3);
560 
561  print_info = gnc_account_print_info (acct, TRUE);
562 
563  return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (b3, print_info));
564 }
565 
566 static gboolean
567 row_changed_foreach_func (GtkTreeModel *model, GtkTreePath *path,
568  GtkTreeIter *iter, gpointer user_data)
569 {
570  gtk_tree_model_row_changed (model, path, iter);
571  return FALSE;
572 }
573 
574 void
575 gnc_tree_model_account_clear_cache (GncTreeModelAccount *model)
576 {
577  if (model)
578  {
579  // destroy the cached account values and recreate
580  g_hash_table_destroy (model->account_values_hash);
581  model->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
582  g_free, g_free);
583 
584  gtk_tree_model_foreach (GTK_TREE_MODEL(model), row_changed_foreach_func, NULL);
585  }
586 }
587 
588 static void
589 clear_account_cached_values (GncTreeModelAccount *model, GHashTable *hash, Account *account)
590 {
591  GtkTreeIter iter;
592  gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
593 
594  if (!account)
595  return;
596 
597  // make sure tree view sees the change
598  if (gnc_tree_model_account_get_iter_from_account (model, account, &iter))
599  {
600  GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
601 
602  gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
603  gtk_tree_path_free (path);
604  }
605 
606  guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
607 
608  // loop over the columns and remove any found
609  for (gint col = 0; col <= GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS; col++)
610  {
611  gchar *key = g_strdup_printf ("%s,%d", acct_guid_str, col);
612 
613  g_hash_table_remove (hash, key);
614  g_free (key);
615  }
616 }
617 
618 static void
619 gnc_tree_model_account_clear_cached_values (GncTreeModelAccount *model, Account *account)
620 {
621  Account *parent;
622 
623  // no hash table or account, return
624  if ((!model->account_values_hash) || (!account))
625  return;
626 
627  clear_account_cached_values (model, model->account_values_hash, account);
628  parent = gnc_account_get_parent (account);
629 
630  // clear also all parent accounts, this will update any balances/totals
631  while (parent)
632  {
633  clear_account_cached_values (model, model->account_values_hash, parent);
634  parent = gnc_account_get_parent (parent);
635  }
636 }
637 
638 static gboolean
639 gnc_tree_model_account_get_cached_value (GncTreeModelAccount *model, Account *account,
640  gint column, gchar **cached_string)
641 {
642  gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
643  gchar *key = NULL;
644  gpointer value;
645  gboolean found;
646 
647  if ((!model->account_values_hash) || (!account))
648  return FALSE;
649 
650  guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
651  key = g_strdup_printf ("%s,%d", acct_guid_str, column);
652 
653  found = g_hash_table_lookup_extended (model->account_values_hash, key,
654  NULL, &value);
655 
656  if (found)
657  *cached_string = g_strdup (value);
658 
659  g_free (key);
660 
661  return found;
662 }
663 
664 static void
665 gnc_tree_model_account_set_cached_value (GncTreeModelAccount *model, Account *account,
666  gint column, GValue *value)
667 {
668  if ((!model->account_values_hash) || (!account))
669  return;
670 
671  // only interested in string values
672  if (G_VALUE_HOLDS_STRING(value))
673  {
674  gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
675  const gchar *str = g_value_get_string (value);
676  gchar *key = NULL;
677 
678  guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
679  key = g_strdup_printf ("%s,%d", acct_guid_str, column);
680 
681  g_hash_table_insert (model->account_values_hash, key, g_strdup (str));
682  }
683 }
684 
685 static void
686 gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
687  GtkTreeIter *iter,
688  int column,
689  GValue *value)
690 {
691  GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
692  Account *account;
693  gboolean negative; /* used to set "deficit style" also known as red numbers */
694  gchar *string;
695  gchar *cached_string = NULL;
696 
697  time64 last_date;
698 
699  g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model));
700  g_return_if_fail (iter != NULL);
701  g_return_if_fail (iter->user_data != NULL);
702  g_return_if_fail (iter->stamp == model->stamp);
703 
704  ENTER("model %p, iter %s, col %d", tree_model,
705  iter_to_string (iter), column);
706 
707  account = (Account *) iter->user_data;
708 
709  // lets see if the value is in the cache
710  if (gnc_tree_model_account_get_cached_value (model, account, column, &cached_string))
711  {
712  g_value_init (value, G_TYPE_STRING);
713  g_value_take_string (value, cached_string);
714  LEAVE("value in cache, '%s'", cached_string);
715  return;
716  }
717 
718  switch (column)
719  {
720  case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
721  g_value_init (value, G_TYPE_STRING);
722  if (account == model->root)
723  g_value_set_string (value, _("New top level account"));
724  else
725  g_value_set_string (value, xaccAccountGetName (account));
726  break;
727  case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
728  g_value_init (value, G_TYPE_STRING);
729  g_value_set_string (value,
731  break;
732  case GNC_TREE_MODEL_ACCOUNT_COL_CODE:
733  g_value_init (value, G_TYPE_STRING);
734  g_value_set_string (value, xaccAccountGetCode (account));
735  break;
736  case GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY:
737  g_value_init (value, G_TYPE_STRING);
738  g_value_set_string (value,
740  break;
741  case GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION:
742  g_value_init (value, G_TYPE_STRING);
743  g_value_set_string (value, xaccAccountGetDescription (account));
744  break;
745 
746  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT:
747  g_value_init (value, G_TYPE_STRING);
748  string = gnc_ui_account_get_print_balance (xaccAccountGetPresentBalanceInCurrency,
749  account, TRUE, &negative);
750  g_value_take_string (value, string);
751  break;
752  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT:
753  g_value_init (value, G_TYPE_STRING);
754  string = gnc_ui_account_get_print_report_balance (xaccAccountGetPresentBalanceInCurrency,
755  account, TRUE, &negative);
756  g_value_take_string (value, string);
757  break;
758  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT:
759  g_value_init (value, G_TYPE_STRING);
760  string = gnc_ui_account_get_print_balance (xaccAccountGetPresentBalanceInCurrency,
761  account, TRUE, &negative);
762  gnc_tree_model_account_set_color (model, negative, value);
763  g_free (string);
764  break;
765 
766  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
767  g_value_init (value, G_TYPE_STRING);
768  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
769  account, FALSE, &negative);
770  g_value_take_string (value, string);
771  break;
772  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
773  g_value_init (value, G_TYPE_STRING);
774  string = gnc_ui_account_get_print_report_balance (xaccAccountGetBalanceInCurrency,
775  account, FALSE, &negative);
776  g_value_take_string (value, string);
777  break;
778  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE:
779  g_value_init (value, G_TYPE_STRING);
780  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
781  account, FALSE, &negative);
782  gnc_tree_model_account_set_color (model, negative, value);
783  g_free (string);
784  break;
785  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
786  g_value_init (value, G_TYPE_STRING);
787  string = gnc_tree_model_account_compute_period_balance (model, account, FALSE, &negative);
788  g_value_take_string (value, string);
789  break;
790  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD:
791  g_value_init (value, G_TYPE_STRING);
792  string = gnc_tree_model_account_compute_period_balance (model, account, FALSE, &negative);
793  gnc_tree_model_account_set_color (model, negative, value);
794  g_free (string);
795  break;
796 
797  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
798  g_value_init (value, G_TYPE_STRING);
799  string = gnc_ui_account_get_balance_limit_icon_name (account);
800  g_value_take_string (value, string);
801  break;
802 
803  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT_EXPLANATION:
804  g_value_init (value, G_TYPE_STRING);
805  string = gnc_ui_account_get_balance_limit_explanation (account);
806  g_value_take_string (value, string);
807  break;
808 
809  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
810  g_value_init (value, G_TYPE_STRING);
811  string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,
812  account, TRUE, &negative);
813  g_value_take_string (value, string);
814  break;
815  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
816  g_value_init (value, G_TYPE_STRING);
817  string = gnc_ui_account_get_print_report_balance (xaccAccountGetClearedBalanceInCurrency,
818  account, TRUE, &negative);
819  g_value_take_string (value, string);
820  break;
821  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED:
822  g_value_init (value, G_TYPE_STRING);
823  string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,
824  account, TRUE, &negative);
825  gnc_tree_model_account_set_color (model, negative, value);
826  g_free (string);
827  break;
828 
829  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
830  g_value_init (value, G_TYPE_STRING);
831  string = gnc_ui_account_get_print_balance (xaccAccountGetReconciledBalanceInCurrency,
832  account, TRUE, &negative);
833  g_value_take_string (value, string);
834  break;
835  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT:
836  g_value_init (value, G_TYPE_STRING);
837  string = gnc_ui_account_get_print_report_balance (xaccAccountGetReconciledBalanceInCurrency,
838  account, TRUE, &negative);
839  g_value_take_string (value, string);
840  break;
841  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE:
842  g_value_init (value, G_TYPE_STRING);
843  if (xaccAccountGetReconcileLastDate (account, &last_date))
844  g_value_take_string (value, qof_print_date (last_date));
845  break;
846 
847  case GNC_TREE_MODEL_ACCOUNT_COL_EARLIEST_DATE:
848  g_value_init (value, G_TYPE_STRING);
849  time64 earliest = gnc_account_get_earliest_date (account);
850  if (earliest != INT64_MAX)
851  g_value_take_string (value, qof_print_date (earliest));
852  break;
853 
854  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED:
855  g_value_init (value, G_TYPE_STRING);
856  string = gnc_ui_account_get_print_balance (xaccAccountGetReconciledBalanceInCurrency,
857  account, TRUE, &negative);
858  gnc_tree_model_account_set_color (model, negative, value);
859  g_free (string);
860  break;
861 
862  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN:
863  g_value_init (value, G_TYPE_STRING);
864  string = gnc_ui_account_get_print_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
865  account, TRUE, &negative);
866  g_value_take_string (value, string);
867  break;
868  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT:
869  g_value_init (value, G_TYPE_STRING);
870  string = gnc_ui_account_get_print_report_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
871  account, TRUE, &negative);
872  g_value_take_string (value, string);
873  break;
874  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN:
875  g_value_init (value, G_TYPE_STRING);
876  string = gnc_ui_account_get_print_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
877  account, TRUE, &negative);
878  gnc_tree_model_account_set_color (model, negative, value);
879  g_free (string);
880  break;
881 
882  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL:
883  g_value_init (value, G_TYPE_STRING);
884  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
885  account, TRUE, &negative);
886  g_value_take_string (value, string);
887  break;
888  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT:
889  g_value_init (value, G_TYPE_STRING);
890  string = gnc_ui_account_get_print_report_balance (xaccAccountGetBalanceInCurrency,
891  account, TRUE, &negative);
892  g_value_take_string (value, string);
893  break;
894  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL:
895  g_value_init (value, G_TYPE_STRING);
896  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
897  account, TRUE, &negative);
898  gnc_tree_model_account_set_color (model, negative, value);
899  g_free (string);
900  break;
901  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD:
902  g_value_init (value, G_TYPE_STRING);
903  string = gnc_tree_model_account_compute_period_balance (model, account, TRUE, &negative);
904  g_value_take_string (value, string);
905  break;
906  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD:
907  g_value_init (value, G_TYPE_STRING);
908  string = gnc_tree_model_account_compute_period_balance (model, account, TRUE, &negative);
909  gnc_tree_model_account_set_color (model, negative, value);
910  g_free (string);
911  break;
912 
913  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT:
914  g_value_init (value, G_TYPE_STRING);
915  g_value_set_string (value, xaccAccountGetColor (account));
916  break;
917 
918  case GNC_TREE_MODEL_ACCOUNT_COL_NOTES:
919  g_value_init (value, G_TYPE_STRING);
920  g_value_set_string (value, xaccAccountGetNotes (account));
921  break;
922 
923  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO:
924  g_value_init (value, G_TYPE_STRING);
925  g_value_take_string (value, gnc_ui_account_get_tax_info_string (account));
926  break;
927 
928  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT:
929  g_value_init (value, G_TYPE_STRING);
930  g_value_take_string (value, gnc_ui_account_get_tax_info_sub_acct_string (account));
931  break;
932 
933  case GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM:
934  g_value_init (value, G_TYPE_STRING);
935  g_value_set_string (value, xaccAccountGetLastNum (account));
936  break;
937 
938  case GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN:
939  g_value_init (value, G_TYPE_BOOLEAN);
940  g_value_set_boolean (value, xaccAccountGetHidden (account));
941  break;
942 
943  case GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER:
944  g_value_init (value, G_TYPE_BOOLEAN);
945  g_value_set_boolean (value, xaccAccountGetPlaceholder (account));
946  break;
947 
948  case GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE:
949  g_value_init (value, G_TYPE_BOOLEAN);
950  g_value_set_boolean (value, xaccAccountGetIsOpeningBalance (account));
951  break;
952 
953  default:
954  g_assert_not_reached ();
955  break;
956  }
957 
958  // save the value to the account values cache
959  gnc_tree_model_account_set_cached_value (model, account, column, value);
960 
961  LEAVE(" ");
962 }
963 
964 static gboolean
965 gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
966  GtkTreeIter *iter)
967 {
968  GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
969  Account *account, *parent;
970  gint i;
971 
972  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), FALSE);
973  g_return_val_if_fail (iter != NULL, FALSE);
974  g_return_val_if_fail (iter->user_data != NULL, FALSE);
975  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
976 
977  ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
978 
979  parent = (Account *) iter->user_data2;
980  if (parent == NULL)
981  {
982  /* This is the root. There is no next. */
983  LEAVE("at root");
984  return FALSE;
985  }
986 
987  /* Get the *next* sibling account. */
988  i = GPOINTER_TO_INT(iter->user_data3);
989  account = gnc_account_nth_child (parent, i + 1);
990  if (account == NULL)
991  {
992  iter->stamp = 0;
993  LEAVE("failed (3)");
994  return FALSE;
995  }
996 
997  iter->user_data = account;
998  iter->user_data2 = parent;
999  iter->user_data3 = GINT_TO_POINTER(i + 1);
1000 
1001  LEAVE("iter %s", iter_to_string (iter));
1002  return TRUE;
1003 }
1004 
1005 static gboolean
1006 gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
1007  GtkTreeIter *iter,
1008  GtkTreeIter *parent_iter)
1009 {
1010  GncTreeModelAccount *model;
1011  Account *account, *parent;
1012 
1013  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1014  ENTER("model %p, iter %p (to be filed in), parent %s",
1015  tree_model, iter, (parent_iter ? iter_to_string (parent_iter) : "(null)"));
1016 
1017  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1018 
1019  if (model->root == NULL)
1020  {
1021  iter->stamp = 0;
1022  LEAVE("failed (no root)");
1023  return FALSE;
1024  }
1025 
1026  /* Special case when no parent supplied. */
1027  if (!parent_iter)
1028  {
1029  iter->user_data = model->root;
1030  iter->user_data2 = NULL;
1031  iter->user_data3 = GINT_TO_POINTER(0);
1032  iter->stamp = model->stamp;
1033  LEAVE("iter (2) %s", iter_to_string (iter));
1034  return TRUE;
1035  }
1036 
1037  gnc_leave_return_val_if_fail (parent_iter != NULL, FALSE);
1038  gnc_leave_return_val_if_fail (parent_iter->user_data != NULL, FALSE);
1039  gnc_leave_return_val_if_fail (parent_iter->stamp == model->stamp, FALSE);
1040 
1041  parent = (Account *)parent_iter->user_data;
1042  account = gnc_account_nth_child (parent, 0);
1043 
1044  if (account == NULL)
1045  {
1046  iter->stamp = 0;
1047  LEAVE("failed (child account is null)");
1048  return FALSE;
1049  }
1050 
1051  iter->user_data = account;
1052  iter->user_data2 = parent;
1053  iter->user_data3 = GINT_TO_POINTER(0);
1054  iter->stamp = model->stamp;
1055  LEAVE("iter (3) %s", iter_to_string (iter));
1056  return TRUE;
1057 }
1058 
1059 static gboolean
1060 gnc_tree_model_account_iter_has_child (GtkTreeModel *tree_model,
1061  GtkTreeIter *iter)
1062 {
1063  GncTreeModelAccount *model;
1064  Account *account;
1065 
1066  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1067 
1068  ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
1069 
1070  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1071 
1072  gnc_leave_return_val_if_fail (iter != NULL, FALSE);
1073  gnc_leave_return_val_if_fail (iter->user_data != NULL, FALSE);
1074  gnc_leave_return_val_if_fail (iter->stamp == model->stamp, FALSE);
1075 
1076  account = (Account *) iter->user_data;
1077  if (gnc_account_n_children (account) > 0)
1078  {
1079  LEAVE("yes");
1080  return TRUE;
1081  }
1082 
1083  LEAVE("no");
1084  return FALSE;
1085 }
1086 
1087 static int
1088 gnc_tree_model_account_iter_n_children (GtkTreeModel *tree_model,
1089  GtkTreeIter *iter)
1090 {
1091  GncTreeModelAccount *model;
1092  gint num;
1093 
1094  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1095  ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
1096 
1097  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1098 
1099  if (iter == NULL)
1100  {
1101  /* How many children does the invisible root node
1102  * have. One! Its the real root account node. */
1103  LEAVE("count is 1");
1104  return 1;
1105  }
1106 
1107  gnc_leave_return_val_if_fail (iter != NULL, FALSE);
1108  gnc_leave_return_val_if_fail (iter->user_data != NULL, FALSE);
1109  gnc_leave_return_val_if_fail (iter->stamp == model->stamp, FALSE);
1110 
1111  num = gnc_account_n_children (iter->user_data);
1112  LEAVE("count is %d", num);
1113  return num;
1114 }
1115 
1116 static gboolean
1117 gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
1118  GtkTreeIter *iter,
1119  GtkTreeIter *parent_iter,
1120  int n)
1121 {
1122  GncTreeModelAccount *model;
1123  Account *account, *parent;
1124 
1125  if (parent_iter)
1126  {
1127  gchar *parent_string;
1128  parent_string = g_strdup (iter_to_string (parent_iter));
1129  ENTER("model %p, iter %s, parent_iter %s, n %d",
1130  tree_model, iter_to_string (iter),
1131  parent_string, n);
1132  g_free (parent_string);
1133  }
1134  else
1135  {
1136  ENTER("model %p, iter %s, parent_iter (null), n %d",
1137  tree_model, iter_to_string (iter), n);
1138  }
1139  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1140 
1141  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1142 
1143  /* Special case when no parent supplied. */
1144  if (!parent_iter)
1145  {
1146  if (n != 0)
1147  {
1148  LEAVE("bad root index");
1149  return FALSE;
1150  }
1151 
1152  iter->user_data = model->root;
1153  iter->user_data2 = NULL;
1154  iter->user_data3 = GINT_TO_POINTER(0);
1155  iter->stamp = model->stamp;
1156  LEAVE("root %s", iter_to_string (iter));
1157  return TRUE;
1158  }
1159 
1160  gnc_leave_return_val_if_fail (parent_iter->user_data != NULL, FALSE);
1161  gnc_leave_return_val_if_fail (parent_iter->stamp == model->stamp, FALSE);
1162 
1163  parent = (Account *)parent_iter->user_data;
1164  account = gnc_account_nth_child (parent, n);
1165  if (account == NULL)
1166  {
1167  iter->stamp = 0;
1168  LEAVE("failed (2)");
1169  return FALSE;
1170  }
1171 
1172  iter->user_data = account;
1173  iter->user_data2 = parent;
1174  iter->user_data3 = GINT_TO_POINTER(n);
1175  iter->stamp = model->stamp;
1176  LEAVE("iter (2) %s", iter_to_string (iter));
1177  return TRUE;
1178 }
1179 
1180 static gboolean
1181 gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
1182  GtkTreeIter *iter,
1183  GtkTreeIter *child)
1184 {
1185  GncTreeModelAccount *model;
1186  Account *account, *parent;
1187  gint i;
1188 
1189  if (child)
1190  {
1191  gchar *child_string;
1192 
1193  child_string = g_strdup (iter_to_string (child));
1194  ENTER("model %p, iter %s, child %s",
1195  tree_model, iter_to_string (iter),
1196  child_string);
1197  g_free (child_string);
1198  }
1199  else
1200  {
1201  ENTER("model %p, iter %s, child (null)",
1202  tree_model, iter_to_string (iter));
1203  }
1204  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1205 
1206  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1207 
1208  gnc_leave_return_val_if_fail (child != NULL, FALSE);
1209  gnc_leave_return_val_if_fail (child->user_data != NULL, FALSE);
1210  gnc_leave_return_val_if_fail (child->stamp == model->stamp, FALSE);
1211 
1212  account = (Account *) child->user_data;
1213  account = gnc_account_get_parent (account);
1214  if (account == NULL)
1215  {
1216  /* Can't go up from the root node */
1217  iter->stamp = 0;
1218  LEAVE("failed (1)");
1219  return FALSE;
1220  }
1221 
1222  parent = gnc_account_get_parent (account);
1223  if (parent == NULL)
1224  {
1225  /* Now at the root. */
1226  i = 0;
1227  }
1228  else
1229  {
1230  i = gnc_account_child_index (parent, account);
1231  }
1232  iter->user_data = account;
1233  iter->user_data2 = parent;
1234  iter->user_data3 = GINT_TO_POINTER(i);
1235  iter->stamp = model->stamp;
1236  LEAVE("iter (2) %s", iter_to_string (iter));
1237  return TRUE;
1238 }
1239 
1240 
1241 /************************************************************/
1242 /* Account Tree View Filter Functions */
1243 /************************************************************/
1244 
1245 /*
1246  * Convert a model/iter pair to a gnucash account. This routine should
1247  * only be called from an account tree view filter function.
1248  */
1249 Account *
1250 gnc_tree_model_account_get_account (GncTreeModelAccount *model,
1251  GtkTreeIter *iter)
1252 {
1253  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
1254  g_return_val_if_fail (iter != NULL, NULL);
1255  g_return_val_if_fail (iter->user_data != NULL, NULL);
1256  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
1257 
1258  return (Account *) iter->user_data;
1259 }
1260 
1261 /*
1262  * Convert a model/account pair into a gtk_tree_model_iter. This
1263  * routine should only be called from the file
1264  * gnc-tree-view-account.c.
1265  */
1266 gboolean
1268  Account *account,
1269  GtkTreeIter *iter)
1270 {
1271  Account *parent;
1272  gint i;
1273 
1274  ENTER("model %p, account %p, iter %p", model, account, iter);
1275  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), FALSE);
1276  gnc_leave_return_val_if_fail ((account != NULL), FALSE);
1277  gnc_leave_return_val_if_fail ((iter != NULL), FALSE);
1278 
1279  iter->user_data = account;
1280  iter->stamp = model->stamp;
1281 
1282  if (account == model->root)
1283  {
1284  iter->user_data2 = NULL;
1285  iter->user_data3 = GINT_TO_POINTER(0);
1286  LEAVE("Matched root");
1287  return TRUE;
1288  }
1289 
1290  if (model->root != gnc_account_get_root (account))
1291  {
1292  LEAVE("Root doesn't match");
1293  return FALSE;
1294  }
1295 
1296  parent = gnc_account_get_parent (account);
1297  i = gnc_account_child_index (parent, account);
1298  iter->user_data2 = parent;
1299  iter->user_data3 = GINT_TO_POINTER(i);
1300  LEAVE("iter %s", iter_to_string (iter));
1301  return (i != -1);
1302 }
1303 
1304 /*
1305  * Convert a model/account pair into a gtk_tree_model_path. This
1306  * routine should only be called from the file
1307  * gnc-tree-view-account.c.
1308  */
1309 GtkTreePath *
1311  Account *account)
1312 {
1313  GtkTreeIter tree_iter;
1314  GtkTreePath *tree_path;
1315 
1316  ENTER("model %p, account %p", model, account);
1317  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
1318  gnc_leave_return_val_if_fail (account != NULL, NULL);
1319 
1320  if (!gnc_tree_model_account_get_iter_from_account (model, account,
1321  &tree_iter))
1322  {
1323  LEAVE("no iter");
1324  return NULL;
1325  }
1326 
1327  tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
1328  if (tree_path)
1329  {
1330  gchar *path_string = gtk_tree_path_to_string (tree_path);
1331  LEAVE("path (2) %s", path_string);
1332  g_free (path_string);
1333  }
1334  else
1335  {
1336  LEAVE("no path");
1337  }
1338  return tree_path;
1339 }
1340 
1341 /************************************************************/
1342 /* Account Tree Model - Engine Event Handling Functions */
1343 /************************************************************/
1344 
1345 static void
1346 increment_stamp (GncTreeModelAccount *model)
1347 {
1348  do model->stamp++;
1349  while (!model->stamp);
1350 }
1351 
1352 static void
1353 propagate_change (GtkTreeModel *model, GtkTreePath *path, gint toggle_if_num)
1354 {
1355  GtkTreeIter iter;
1356 
1357  /* Already at the invisible root node? */
1358  if (!gtk_tree_path_up (path))
1359  return;
1360 
1361  /* Did we just move up to the invisible root node? */
1362  if (gtk_tree_path_get_depth (path) == 0)
1363  return;
1364 
1365  /* Handle the immediate parent */
1366  if (gtk_tree_model_get_iter (model, &iter, path))
1367  {
1368  gtk_tree_model_row_changed (model, path, &iter);
1369  if (gtk_tree_model_iter_n_children (model, &iter) == toggle_if_num)
1370  gtk_tree_model_row_has_child_toggled (model, path, &iter);
1371  }
1372 
1373  /* All other ancestors */
1374  while (gtk_tree_path_up (path) && gtk_tree_path_get_depth (path) > 0 &&
1375  gtk_tree_model_get_iter (model, &iter, path))
1376  {
1377  gtk_tree_model_row_changed (model, path, &iter);
1378  }
1379 }
1380 
1409 static void
1410 gnc_tree_model_account_event_handler (QofInstance *entity,
1411  QofEventId event_type,
1412  GncTreeModelAccount *model,
1413  GncEventData *ed)
1414 {
1415  const gchar *parent_name;
1416  GtkTreePath *path = NULL;
1417  GtkTreeIter iter;
1418  Account *account, *parent;
1419 
1420  g_return_if_fail (model); /* Required */
1421 
1422  if (!GNC_IS_ACCOUNT(entity))
1423  return;
1424 
1425  ENTER("entity %p of type %d, model %p, event_data %p",
1426  entity, event_type, model, ed);
1427 
1428  account = GNC_ACCOUNT(entity);
1429 
1430  if (gnc_account_get_book (account) != model->book)
1431  {
1432  LEAVE("not in this book");
1433  return;
1434  }
1435  if (gnc_account_get_root (account) != model->root)
1436  {
1437  LEAVE("not in this model");
1438  return;
1439  }
1440 
1441  switch (event_type)
1442  {
1443  case QOF_EVENT_ADD:
1444  /* Tell the filters/views where the new account was added. */
1445  DEBUG("add account %p (%s)", account, xaccAccountGetName (account));
1446  path = gnc_tree_model_account_get_path_from_account (model, account);
1447  if (!path)
1448  {
1449  DEBUG("can't generate path");
1450  break;
1451  }
1452  increment_stamp (model);
1453  if (!gnc_tree_model_account_get_iter (GTK_TREE_MODEL(model), &iter, path))
1454  {
1455  DEBUG("can't generate iter");
1456  break;
1457  }
1458  gtk_tree_model_row_inserted (GTK_TREE_MODEL(model), path, &iter);
1459  propagate_change (GTK_TREE_MODEL(model), path, 1);
1460  break;
1461 
1462  case QOF_EVENT_REMOVE:
1463  if (!ed) /* Required for a remove. */
1464  break;
1465  parent = ed->node ? GNC_ACCOUNT(ed->node) : model->root;
1466  parent_name = ed->node ? xaccAccountGetName (parent) : "Root";
1467  DEBUG("remove child %d of account %p (%s)", ed->idx, parent, parent_name);
1468  path = gnc_tree_model_account_get_path_from_account (model, parent);
1469  if (!path)
1470  {
1471  DEBUG("can't generate path");
1472  break;
1473  }
1474  increment_stamp (model);
1475  gtk_tree_path_append_index (path, ed->idx);
1476  gtk_tree_model_row_deleted (GTK_TREE_MODEL(model), path);
1477  propagate_change (GTK_TREE_MODEL(model), path, 0);
1478  break;
1479 
1480  case QOF_EVENT_MODIFY:
1481  DEBUG("modify account %p (%s)", account, xaccAccountGetName (account));
1482  /* clear the cached model values for account */
1483  gnc_tree_model_account_clear_cached_values(model, account);
1484 
1485  path = gnc_tree_model_account_get_path_from_account(model, account);
1486  if (!path)
1487  {
1488  DEBUG("can't generate path");
1489  break;
1490  }
1491  if (!gnc_tree_model_account_get_iter (GTK_TREE_MODEL(model), &iter, path))
1492  {
1493  DEBUG("can't generate iter");
1494  break;
1495  }
1496  gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
1497  propagate_change (GTK_TREE_MODEL(model), path, -1);
1498  break;
1499 
1500  default:
1501  gnc_tree_model_account_clear_cached_values(model, account);
1502 
1503  LEAVE("unknown event type");
1504  return;
1505  }
1506 
1507  if (path)
1508  gtk_tree_path_free (path);
1509  LEAVE(" ");
1510  return;
1511 }
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2902
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition: Account.cpp:4635
The instance data structure for an account tree model.
const char * gnc_print_amount_with_bidi_ltr_isolate(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
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.cpp:127
void(* QofEventHandler)(QofInstance *ent, QofEventId event_type, gpointer handler_data, gpointer event_data)
Handler invoked when an event is generated.
Definition: qofevent.h:89
utility functions for the GnuCash UI
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3234
const char * xaccAccountGetCode(const Account *acc)
Get the account&#39;s accounting code.
Definition: Account.cpp:3303
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
STRUCTS.
GncTreeModel gnc_tree_model
The parent object data.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
Account * gnc_tree_model_account_get_account(GncTreeModelAccount *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash account.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:208
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
#define gnc_leave_return_val_if_fail(test, val)
Replacement for g_return_val_if_fail, but calls LEAVE if the test fails.
Definition: qoflog.h:294
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.
G_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_APPLICATION_WINDOW, G_IMPLEMENT_INTERFACE(GNC_TYPE_WINDOW, gnc_window_main_window_init)) static guint main_window_signals[LAST_SIGNAL]
A holding place for all the signals generated by the main window code.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
const char * xaccAccountGetColor(const Account *acc)
Get the account&#39;s color.
Definition: Account.cpp:3317
GtkTreeModel implementation for gnucash account tree.
#define xaccAccountGetGUID(X)
Definition: Account.h:252
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:608
void gnc_tree_model_account_clear_cache(GncTreeModelAccount *model)
Clear the tree model account cached values.
Account handling public routines.
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
Gobject helper routines.
GtkTreeModel * gnc_tree_model_account_new(Account *root)
Create a new GtkTreeModel for manipulating gnucash accounts.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
const char * xaccAccountGetDescription(const Account *acc)
Get the account&#39;s description.
Definition: Account.cpp:3310
time64 gnc_account_get_earliest_date(const Account *account)
Returns the date of the earliest split in the account, or INT64_MAX.
Definition: Account.cpp:5082
General utilities for dealing with accounting periods.
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
int stamp
The state of the model.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
Additional event handling code.
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition: Account.cpp:2943
All type declarations for the whole Gnucash engine.
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition: Account.cpp:4518
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition: Account.cpp:4142
Generic api to store and retrieve preferences.
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition: Account.cpp:4095
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3368
GtkTreePath * gnc_tree_model_account_get_path_from_account(GncTreeModelAccount *model, Account *account)
Convert a model/account pair into a gtk_tree_model_path.
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition: Account.cpp:4071
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Account * gnc_account_nth_child(const Account *parent, gint num)
Return the n&#39;th child account of the specified parent account.
Definition: Account.cpp:2960
gint gnc_account_child_index(const Account *parent, const Account *child)
Return the index of the specified child within the list of the parent&#39;s children. ...
Definition: Account.cpp:2950
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
Account * gnc_account_get_root(Account *acc)
This routine returns the root account of the account tree that the specified account belongs to...
Definition: Account.cpp:2909
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3256
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition: Account.cpp:4309
Commodity handling public routines.
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.cpp:142
const char * xaccAccountGetNotes(const Account *acc)
Get the account&#39;s notes.
Definition: Account.cpp:3341