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  G_OBJECT_CLASS(gnc_tree_model_account_parent_class)->dispose (object);
246  LEAVE(" ");
247 }
248 
249 
250 /************************************************************/
251 /* New Model Creation */
252 /************************************************************/
253 
254 GtkTreeModel *
256 {
257  GncTreeModelAccount *model;
258  const GList *item;
259 
260  ENTER("root %p", root);
261  item = gnc_gobject_tracking_get_list (GNC_TREE_MODEL_ACCOUNT_NAME);
262  for ( ; item; item = g_list_next (item))
263  {
264  model = (GncTreeModelAccount *)item->data;
265  if (model->root == root)
266  {
267  g_object_ref (G_OBJECT(model));
268  LEAVE("returning existing model %p", model);
269  return GTK_TREE_MODEL(model);
270  }
271  }
272 
273  model = g_object_new (GNC_TYPE_TREE_MODEL_ACCOUNT, NULL);
274 
275  model->book = gnc_get_current_book();
276  model->root = root;
277 
278  model->event_handler_id = qof_event_register_handler
279  ((QofEventHandler)gnc_tree_model_account_event_handler, model);
280 
281  LEAVE("model %p", model);
282  return GTK_TREE_MODEL(model);
283 }
284 
285 
286 /************************************************************/
287 /* Gnc Tree Model Debugging Utility Function */
288 /************************************************************/
289 
290 #define ITER_STRING_LEN 128
291 
292 static const gchar *
293 iter_to_string (GtkTreeIter *iter)
294 {
295 #ifdef G_THREADS_ENABLED
296  static GPrivate gtmits_buffer_key = G_PRIVATE_INIT(g_free);
297  gchar *string;
298 
299  string = g_private_get (&gtmits_buffer_key);
300  if (string == NULL)
301  {
302  string = g_malloc(ITER_STRING_LEN + 1);
303  g_private_set (&gtmits_buffer_key, string);
304  }
305 #else
306  static char string[ITER_STRING_LEN + 1];
307 #endif
308 
309  if (iter)
310  snprintf (string, ITER_STRING_LEN,
311  "[stamp:%x data:%p (%s), %p, %d]",
312  iter->stamp, iter->user_data,
313  xaccAccountGetName ((Account *) iter->user_data),
314  iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
315  else
316  strcpy (string, "(null)");
317  return string;
318 }
319 
320 
321 /************************************************************/
322 /* Gtk Tree Model Required Interface Functions */
323 /************************************************************/
324 
325 static void
326 gnc_tree_model_account_tree_model_init (GtkTreeModelIface *iface)
327 {
328  iface->get_flags = gnc_tree_model_account_get_flags;
329  iface->get_n_columns = gnc_tree_model_account_get_n_columns;
330  iface->get_column_type = gnc_tree_model_account_get_column_type;
331  iface->get_iter = gnc_tree_model_account_get_iter;
332  iface->get_path = gnc_tree_model_account_get_path;
333  iface->get_value = gnc_tree_model_account_get_value;
334  iface->iter_next = gnc_tree_model_account_iter_next;
335  iface->iter_children = gnc_tree_model_account_iter_children;
336  iface->iter_has_child = gnc_tree_model_account_iter_has_child;
337  iface->iter_n_children = gnc_tree_model_account_iter_n_children;
338  iface->iter_nth_child = gnc_tree_model_account_iter_nth_child;
339  iface->iter_parent = gnc_tree_model_account_iter_parent;
340 }
341 
342 static GtkTreeModelFlags
343 gnc_tree_model_account_get_flags (GtkTreeModel *tree_model)
344 {
345  return 0;
346 }
347 
348 static int
349 gnc_tree_model_account_get_n_columns (GtkTreeModel *tree_model)
350 {
351  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), -1);
352 
353  return GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS;
354 }
355 
356 static GType
357 gnc_tree_model_account_get_column_type (GtkTreeModel *tree_model, int index)
358 {
359  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), G_TYPE_INVALID);
360  g_return_val_if_fail ((index < GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID);
361 
362  switch (index)
363  {
364  case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
365  case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
366  case GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY:
367  case GNC_TREE_MODEL_ACCOUNT_COL_CODE:
368  case GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION:
369  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT:
370  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT:
371  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
372  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
373  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
374  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
375  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT_EXPLANATION:
376  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
377  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
378  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
379  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT:
380  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE:
381  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN:
382  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT:
383  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL:
384  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT:
385  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD:
386  case GNC_TREE_MODEL_ACCOUNT_COL_NOTES:
387  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO:
388  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT:
389  case GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM:
390 
391  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT:
392  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT:
393  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE:
394  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD:
395  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED:
396  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED:
397  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN:
398  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL:
399  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD:
400  return G_TYPE_STRING;
401 
402  case GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN:
403  case GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER:
404  case GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE:
405  return G_TYPE_BOOLEAN;
406 
407  default:
408  g_assert_not_reached ();
409  return G_TYPE_INVALID;
410  }
411 }
412 
413 static gboolean
414 gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
415  GtkTreeIter *iter,
416  GtkTreePath *path)
417 {
418  GncTreeModelAccount *model;
419  Account *account, *parent;
420  gint i, *indices;
421 
422  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
423 
424  {
425  gchar *path_string = gtk_tree_path_to_string (path);
426  ENTER("model %p, iter %p, path %s", tree_model, iter, path_string);
427  g_free (path_string);
428  }
429 
430  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
431 
432  if (gtk_tree_path_get_depth (path) <= 0)
433  {
434  LEAVE("bad depth");
435  return FALSE;
436  }
437 
438  indices = gtk_tree_path_get_indices (path);
439  if (indices[0] != 0)
440  {
441  LEAVE("bad root index");
442  return FALSE;
443  }
444 
445  parent = NULL;
446  account = model->root;
447  for (i = 1; i < gtk_tree_path_get_depth (path); i++)
448  {
449  parent = account;
450  account = gnc_account_nth_child (parent, indices[i]);
451  if (account == NULL)
452  {
453  iter->stamp = 0;
454  LEAVE("bad index");
455  return FALSE;
456  }
457  }
458 
459  iter->stamp = model->stamp;
460  iter->user_data = account;
461  iter->user_data2 = parent;
462  iter->user_data3 = GINT_TO_POINTER(indices[i - 1]);
463 
464  LEAVE("iter %s", iter_to_string (iter));
465  return TRUE;
466 }
467 
468 static GtkTreePath *
469 gnc_tree_model_account_get_path (GtkTreeModel *tree_model,
470  GtkTreeIter *iter)
471 {
472  GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
473  Account *account, *parent;
474  GtkTreePath *path;
475  gint i;
476 
477  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
478  g_return_val_if_fail (iter != NULL, NULL);
479  g_return_val_if_fail (iter->user_data != NULL, NULL);
480  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
481 
482  ENTER("model %p, iter %s", model, iter_to_string (iter));
483 
484  if (model->root == NULL)
485  {
486  LEAVE("failed (1)");
487  return NULL;
488  }
489 
490  account = (Account *) iter->user_data;
491  parent = (Account *) iter->user_data2;
492 
493  path = gtk_tree_path_new ();
494  while (parent)
495  {
496  i = gnc_account_child_index (parent, account);
497  if (i == -1)
498  {
499  gtk_tree_path_free (path);
500  LEAVE("failed (3)");
501  return NULL;
502  }
503  gtk_tree_path_prepend_index (path, i);
504  account = parent;
505  parent = gnc_account_get_parent (account);
506  };
507 
508  /* Add the root node. */
509  gtk_tree_path_prepend_index (path, 0);
510 
511  {
512  gchar *path_string = gtk_tree_path_to_string (path);
513  LEAVE("path (4) %s", path_string);
514  g_free (path_string);
515  }
516  return path;
517 }
518 
519 static void
520 gnc_tree_model_account_set_color (GncTreeModelAccount *model,
521  gboolean negative,
522  GValue *value)
523 {
524  if (negative)
525  g_value_set_static_string (value, model->negative_color);
526  else
527  g_value_set_static_string (value, NULL);
528 }
529 
530 static gchar *
531 gnc_tree_model_account_compute_period_balance (GncTreeModelAccount *model,
532  Account *acct,
533  gboolean recurse,
534  gboolean *negative)
535 {
536  GNCPrintAmountInfo print_info;
537  time64 t1, t2;
538  gnc_numeric b3;
539 
540  if (negative)
541  *negative = FALSE;
542 
543  if (acct == model->root)
544  return g_strdup ("");
545 
546  t1 = gnc_accounting_period_fiscal_start ();
547  t2 = gnc_accounting_period_fiscal_end ();
548 
549  if (t1 > t2)
550  return g_strdup ("");
551 
552  b3 = xaccAccountGetBalanceChangeForPeriod (acct, t1, t2, recurse);
553  if (gnc_reverse_balance (acct))
554  b3 = gnc_numeric_neg (b3);
555 
556  if (negative)
557  *negative = gnc_numeric_negative_p (b3);
558 
559  print_info = gnc_account_print_info (acct, TRUE);
560 
561  return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (b3, print_info));
562 }
563 
564 static gboolean
565 row_changed_foreach_func (GtkTreeModel *model, GtkTreePath *path,
566  GtkTreeIter *iter, gpointer user_data)
567 {
568  gtk_tree_model_row_changed (model, path, iter);
569  return FALSE;
570 }
571 
572 void
573 gnc_tree_model_account_clear_cache (GncTreeModelAccount *model)
574 {
575  if (model)
576  {
577  // destroy the cached account values and recreate
578  g_hash_table_destroy (model->account_values_hash);
579  model->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
580  g_free, g_free);
581 
582  gtk_tree_model_foreach (GTK_TREE_MODEL(model), row_changed_foreach_func, NULL);
583  }
584 }
585 
586 static void
587 clear_account_cached_values (GncTreeModelAccount *model, GHashTable *hash, Account *account)
588 {
589  GtkTreeIter iter;
590  gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
591 
592  if (!account)
593  return;
594 
595  // make sure tree view sees the change
596  if (gnc_tree_model_account_get_iter_from_account (model, account, &iter))
597  {
598  GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
599 
600  gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
601  gtk_tree_path_free (path);
602  }
603 
604  guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
605 
606  // loop over the columns and remove any found
607  for (gint col = 0; col <= GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS; col++)
608  {
609  gchar *key = g_strdup_printf ("%s,%d", acct_guid_str, col);
610 
611  g_hash_table_remove (hash, key);
612  g_free (key);
613  }
614 }
615 
616 static void
617 gnc_tree_model_account_clear_cached_values (GncTreeModelAccount *model, Account *account)
618 {
619  Account *parent;
620 
621  // no hash table or account, return
622  if ((!model->account_values_hash) || (!account))
623  return;
624 
625  clear_account_cached_values (model, model->account_values_hash, account);
626  parent = gnc_account_get_parent (account);
627 
628  // clear also all parent accounts, this will update any balances/totals
629  while (parent)
630  {
631  clear_account_cached_values (model, model->account_values_hash, parent);
632  parent = gnc_account_get_parent (parent);
633  }
634 }
635 
636 static gboolean
637 gnc_tree_model_account_get_cached_value (GncTreeModelAccount *model, Account *account,
638  gint column, gchar **cached_string)
639 {
640  gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
641  gchar *key = NULL;
642  gpointer value;
643  gboolean found;
644 
645  if ((!model->account_values_hash) || (!account))
646  return FALSE;
647 
648  guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
649  key = g_strdup_printf ("%s,%d", acct_guid_str, column);
650 
651  found = g_hash_table_lookup_extended (model->account_values_hash, key,
652  NULL, &value);
653 
654  if (found)
655  *cached_string = g_strdup (value);
656 
657  g_free (key);
658 
659  return found;
660 }
661 
662 static void
663 gnc_tree_model_account_set_cached_value (GncTreeModelAccount *model, Account *account,
664  gint column, GValue *value)
665 {
666  if ((!model->account_values_hash) || (!account))
667  return;
668 
669  // only interested in string values
670  if (G_VALUE_HOLDS_STRING(value))
671  {
672  gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
673  const gchar *str = g_value_get_string (value);
674  gchar *key = NULL;
675 
676  guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
677  key = g_strdup_printf ("%s,%d", acct_guid_str, column);
678 
679  g_hash_table_insert (model->account_values_hash, key, g_strdup (str));
680  }
681 }
682 
683 static void
684 gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
685  GtkTreeIter *iter,
686  int column,
687  GValue *value)
688 {
689  GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
690  Account *account;
691  gboolean negative; /* used to set "deficit style" also known as red numbers */
692  gchar *string;
693  gchar *cached_string = NULL;
694 
695  time64 last_date;
696 
697  g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model));
698  g_return_if_fail (iter != NULL);
699  g_return_if_fail (iter->user_data != NULL);
700  g_return_if_fail (iter->stamp == model->stamp);
701 
702  ENTER("model %p, iter %s, col %d", tree_model,
703  iter_to_string (iter), column);
704 
705  account = (Account *) iter->user_data;
706 
707  // lets see if the value is in the cache
708  if (gnc_tree_model_account_get_cached_value (model, account, column, &cached_string))
709  {
710  g_value_init (value, G_TYPE_STRING);
711  g_value_take_string (value, cached_string);
712  LEAVE("value in cache, '%s'", cached_string);
713  return;
714  }
715 
716  switch (column)
717  {
718  case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
719  g_value_init (value, G_TYPE_STRING);
720  if (account == model->root)
721  g_value_set_string (value, _("New top level account"));
722  else
723  g_value_set_string (value, xaccAccountGetName (account));
724  break;
725  case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
726  g_value_init (value, G_TYPE_STRING);
727  g_value_set_string (value,
729  break;
730  case GNC_TREE_MODEL_ACCOUNT_COL_CODE:
731  g_value_init (value, G_TYPE_STRING);
732  g_value_set_string (value, xaccAccountGetCode (account));
733  break;
734  case GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY:
735  g_value_init (value, G_TYPE_STRING);
736  g_value_set_string (value,
738  break;
739  case GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION:
740  g_value_init (value, G_TYPE_STRING);
741  g_value_set_string (value, xaccAccountGetDescription (account));
742  break;
743 
744  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT:
745  g_value_init (value, G_TYPE_STRING);
746  string = gnc_ui_account_get_print_balance (xaccAccountGetPresentBalanceInCurrency,
747  account, TRUE, &negative);
748  g_value_take_string (value, string);
749  break;
750  case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT:
751  g_value_init (value, G_TYPE_STRING);
752  string = gnc_ui_account_get_print_report_balance (xaccAccountGetPresentBalanceInCurrency,
753  account, TRUE, &negative);
754  g_value_take_string (value, string);
755  break;
756  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT:
757  g_value_init (value, G_TYPE_STRING);
758  string = gnc_ui_account_get_print_balance (xaccAccountGetPresentBalanceInCurrency,
759  account, TRUE, &negative);
760  gnc_tree_model_account_set_color (model, negative, value);
761  g_free (string);
762  break;
763 
764  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
765  g_value_init (value, G_TYPE_STRING);
766  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
767  account, FALSE, &negative);
768  g_value_take_string (value, string);
769  break;
770  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
771  g_value_init (value, G_TYPE_STRING);
772  string = gnc_ui_account_get_print_report_balance (xaccAccountGetBalanceInCurrency,
773  account, FALSE, &negative);
774  g_value_take_string (value, string);
775  break;
776  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE:
777  g_value_init (value, G_TYPE_STRING);
778  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
779  account, FALSE, &negative);
780  gnc_tree_model_account_set_color (model, negative, value);
781  g_free (string);
782  break;
783  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
784  g_value_init (value, G_TYPE_STRING);
785  string = gnc_tree_model_account_compute_period_balance (model, account, FALSE, &negative);
786  g_value_take_string (value, string);
787  break;
788  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD:
789  g_value_init (value, G_TYPE_STRING);
790  string = gnc_tree_model_account_compute_period_balance (model, account, FALSE, &negative);
791  gnc_tree_model_account_set_color (model, negative, value);
792  g_free (string);
793  break;
794 
795  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
796  g_value_init (value, G_TYPE_STRING);
797  string = gnc_ui_account_get_balance_limit_icon_name (account);
798  g_value_take_string (value, string);
799  break;
800 
801  case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT_EXPLANATION:
802  g_value_init (value, G_TYPE_STRING);
803  string = gnc_ui_account_get_balance_limit_explanation (account);
804  g_value_take_string (value, string);
805  break;
806 
807  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
808  g_value_init (value, G_TYPE_STRING);
809  string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,
810  account, TRUE, &negative);
811  g_value_take_string (value, string);
812  break;
813  case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
814  g_value_init (value, G_TYPE_STRING);
815  string = gnc_ui_account_get_print_report_balance (xaccAccountGetClearedBalanceInCurrency,
816  account, TRUE, &negative);
817  g_value_take_string (value, string);
818  break;
819  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED:
820  g_value_init (value, G_TYPE_STRING);
821  string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,
822  account, TRUE, &negative);
823  gnc_tree_model_account_set_color (model, negative, value);
824  g_free (string);
825  break;
826 
827  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
828  g_value_init (value, G_TYPE_STRING);
829  string = gnc_ui_account_get_print_balance (xaccAccountGetReconciledBalanceInCurrency,
830  account, TRUE, &negative);
831  g_value_take_string (value, string);
832  break;
833  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT:
834  g_value_init (value, G_TYPE_STRING);
835  string = gnc_ui_account_get_print_report_balance (xaccAccountGetReconciledBalanceInCurrency,
836  account, TRUE, &negative);
837  g_value_take_string (value, string);
838  break;
839  case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE:
840  g_value_init (value, G_TYPE_STRING);
841  if (xaccAccountGetReconcileLastDate (account, &last_date))
842  g_value_take_string (value, qof_print_date (last_date));
843  break;
844 
845  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED:
846  g_value_init (value, G_TYPE_STRING);
847  string = gnc_ui_account_get_print_balance (xaccAccountGetReconciledBalanceInCurrency,
848  account, TRUE, &negative);
849  gnc_tree_model_account_set_color (model, negative, value);
850  g_free (string);
851  break;
852 
853  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN:
854  g_value_init (value, G_TYPE_STRING);
855  string = gnc_ui_account_get_print_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
856  account, TRUE, &negative);
857  g_value_take_string (value, string);
858  break;
859  case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT:
860  g_value_init (value, G_TYPE_STRING);
861  string = gnc_ui_account_get_print_report_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
862  account, TRUE, &negative);
863  g_value_take_string (value, string);
864  break;
865  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN:
866  g_value_init (value, G_TYPE_STRING);
867  string = gnc_ui_account_get_print_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
868  account, TRUE, &negative);
869  gnc_tree_model_account_set_color (model, negative, value);
870  g_free (string);
871  break;
872 
873  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL:
874  g_value_init (value, G_TYPE_STRING);
875  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
876  account, TRUE, &negative);
877  g_value_take_string (value, string);
878  break;
879  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT:
880  g_value_init (value, G_TYPE_STRING);
881  string = gnc_ui_account_get_print_report_balance (xaccAccountGetBalanceInCurrency,
882  account, TRUE, &negative);
883  g_value_take_string (value, string);
884  break;
885  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL:
886  g_value_init (value, G_TYPE_STRING);
887  string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
888  account, TRUE, &negative);
889  gnc_tree_model_account_set_color (model, negative, value);
890  g_free (string);
891  break;
892  case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD:
893  g_value_init (value, G_TYPE_STRING);
894  string = gnc_tree_model_account_compute_period_balance (model, account, TRUE, &negative);
895  g_value_take_string (value, string);
896  break;
897  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD:
898  g_value_init (value, G_TYPE_STRING);
899  string = gnc_tree_model_account_compute_period_balance (model, account, TRUE, &negative);
900  gnc_tree_model_account_set_color (model, negative, value);
901  g_free (string);
902  break;
903 
904  case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT:
905  g_value_init (value, G_TYPE_STRING);
906  g_value_set_string (value, xaccAccountGetColor (account));
907  break;
908 
909  case GNC_TREE_MODEL_ACCOUNT_COL_NOTES:
910  g_value_init (value, G_TYPE_STRING);
911  g_value_set_string (value, xaccAccountGetNotes (account));
912  break;
913 
914  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO:
915  g_value_init (value, G_TYPE_STRING);
916  g_value_take_string (value, gnc_ui_account_get_tax_info_string (account));
917  break;
918 
919  case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT:
920  g_value_init (value, G_TYPE_STRING);
921  g_value_take_string (value, gnc_ui_account_get_tax_info_sub_acct_string (account));
922  break;
923 
924  case GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM:
925  g_value_init (value, G_TYPE_STRING);
926  g_value_set_string (value, xaccAccountGetLastNum (account));
927  break;
928 
929  case GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN:
930  g_value_init (value, G_TYPE_BOOLEAN);
931  g_value_set_boolean (value, xaccAccountGetHidden (account));
932  break;
933 
934  case GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER:
935  g_value_init (value, G_TYPE_BOOLEAN);
936  g_value_set_boolean (value, xaccAccountGetPlaceholder (account));
937  break;
938 
939  case GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE:
940  g_value_init (value, G_TYPE_BOOLEAN);
941  g_value_set_boolean (value, xaccAccountGetIsOpeningBalance (account));
942  break;
943 
944  default:
945  g_assert_not_reached ();
946  break;
947  }
948 
949  // save the value to the account values cache
950  gnc_tree_model_account_set_cached_value (model, account, column, value);
951 
952  LEAVE(" ");
953 }
954 
955 static gboolean
956 gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
957  GtkTreeIter *iter)
958 {
959  GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
960  Account *account, *parent;
961  gint i;
962 
963  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), FALSE);
964  g_return_val_if_fail (iter != NULL, FALSE);
965  g_return_val_if_fail (iter->user_data != NULL, FALSE);
966  g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
967 
968  ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
969 
970  parent = (Account *) iter->user_data2;
971  if (parent == NULL)
972  {
973  /* This is the root. There is no next. */
974  LEAVE("at root");
975  return FALSE;
976  }
977 
978  /* Get the *next* sibling account. */
979  i = GPOINTER_TO_INT(iter->user_data3);
980  account = gnc_account_nth_child (parent, i + 1);
981  if (account == NULL)
982  {
983  iter->stamp = 0;
984  LEAVE("failed (3)");
985  return FALSE;
986  }
987 
988  iter->user_data = account;
989  iter->user_data2 = parent;
990  iter->user_data3 = GINT_TO_POINTER(i + 1);
991 
992  LEAVE("iter %s", iter_to_string (iter));
993  return TRUE;
994 }
995 
996 static gboolean
997 gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
998  GtkTreeIter *iter,
999  GtkTreeIter *parent_iter)
1000 {
1001  GncTreeModelAccount *model;
1002  Account *account, *parent;
1003 
1004  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1005  ENTER("model %p, iter %p (to be filed in), parent %s",
1006  tree_model, iter, (parent_iter ? iter_to_string (parent_iter) : "(null)"));
1007 
1008  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1009 
1010  if (model->root == NULL)
1011  {
1012  iter->stamp = 0;
1013  LEAVE("failed (no root)");
1014  return FALSE;
1015  }
1016 
1017  /* Special case when no parent supplied. */
1018  if (!parent_iter)
1019  {
1020  iter->user_data = model->root;
1021  iter->user_data2 = NULL;
1022  iter->user_data3 = GINT_TO_POINTER(0);
1023  iter->stamp = model->stamp;
1024  LEAVE("iter (2) %s", iter_to_string (iter));
1025  return TRUE;
1026  }
1027 
1028  gnc_leave_return_val_if_fail (parent_iter != NULL, FALSE);
1029  gnc_leave_return_val_if_fail (parent_iter->user_data != NULL, FALSE);
1030  gnc_leave_return_val_if_fail (parent_iter->stamp == model->stamp, FALSE);
1031 
1032  parent = (Account *)parent_iter->user_data;
1033  account = gnc_account_nth_child (parent, 0);
1034 
1035  if (account == NULL)
1036  {
1037  iter->stamp = 0;
1038  LEAVE("failed (child account is null)");
1039  return FALSE;
1040  }
1041 
1042  iter->user_data = account;
1043  iter->user_data2 = parent;
1044  iter->user_data3 = GINT_TO_POINTER(0);
1045  iter->stamp = model->stamp;
1046  LEAVE("iter (3) %s", iter_to_string (iter));
1047  return TRUE;
1048 }
1049 
1050 static gboolean
1051 gnc_tree_model_account_iter_has_child (GtkTreeModel *tree_model,
1052  GtkTreeIter *iter)
1053 {
1054  GncTreeModelAccount *model;
1055  Account *account;
1056 
1057  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1058 
1059  ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
1060 
1061  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1062 
1063  gnc_leave_return_val_if_fail (iter != NULL, FALSE);
1064  gnc_leave_return_val_if_fail (iter->user_data != NULL, FALSE);
1065  gnc_leave_return_val_if_fail (iter->stamp == model->stamp, FALSE);
1066 
1067  account = (Account *) iter->user_data;
1068  if (gnc_account_n_children (account) > 0)
1069  {
1070  LEAVE("yes");
1071  return TRUE;
1072  }
1073 
1074  LEAVE("no");
1075  return FALSE;
1076 }
1077 
1078 static int
1079 gnc_tree_model_account_iter_n_children (GtkTreeModel *tree_model,
1080  GtkTreeIter *iter)
1081 {
1082  GncTreeModelAccount *model;
1083  gint num;
1084 
1085  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1086  ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
1087 
1088  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1089 
1090  if (iter == NULL)
1091  {
1092  /* How many children does the invisible root node
1093  * have. One! Its the real root account node. */
1094  LEAVE("count is 1");
1095  return 1;
1096  }
1097 
1098  gnc_leave_return_val_if_fail (iter != NULL, FALSE);
1099  gnc_leave_return_val_if_fail (iter->user_data != NULL, FALSE);
1100  gnc_leave_return_val_if_fail (iter->stamp == model->stamp, FALSE);
1101 
1102  num = gnc_account_n_children (iter->user_data);
1103  LEAVE("count is %d", num);
1104  return num;
1105 }
1106 
1107 static gboolean
1108 gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
1109  GtkTreeIter *iter,
1110  GtkTreeIter *parent_iter,
1111  int n)
1112 {
1113  GncTreeModelAccount *model;
1114  Account *account, *parent;
1115 
1116  if (parent_iter)
1117  {
1118  gchar *parent_string;
1119  parent_string = g_strdup (iter_to_string (parent_iter));
1120  ENTER("model %p, iter %s, parent_iter %s, n %d",
1121  tree_model, iter_to_string (iter),
1122  parent_string, n);
1123  g_free (parent_string);
1124  }
1125  else
1126  {
1127  ENTER("model %p, iter %s, parent_iter (null), n %d",
1128  tree_model, iter_to_string (iter), n);
1129  }
1130  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1131 
1132  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1133 
1134  /* Special case when no parent supplied. */
1135  if (!parent_iter)
1136  {
1137  if (n != 0)
1138  {
1139  LEAVE("bad root index");
1140  return FALSE;
1141  }
1142 
1143  iter->user_data = model->root;
1144  iter->user_data2 = NULL;
1145  iter->user_data3 = GINT_TO_POINTER(0);
1146  iter->stamp = model->stamp;
1147  LEAVE("root %s", iter_to_string (iter));
1148  return TRUE;
1149  }
1150 
1151  gnc_leave_return_val_if_fail (parent_iter->user_data != NULL, FALSE);
1152  gnc_leave_return_val_if_fail (parent_iter->stamp == model->stamp, FALSE);
1153 
1154  parent = (Account *)parent_iter->user_data;
1155  account = gnc_account_nth_child (parent, n);
1156  if (account == NULL)
1157  {
1158  iter->stamp = 0;
1159  LEAVE("failed (2)");
1160  return FALSE;
1161  }
1162 
1163  iter->user_data = account;
1164  iter->user_data2 = parent;
1165  iter->user_data3 = GINT_TO_POINTER(n);
1166  iter->stamp = model->stamp;
1167  LEAVE("iter (2) %s", iter_to_string (iter));
1168  return TRUE;
1169 }
1170 
1171 static gboolean
1172 gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
1173  GtkTreeIter *iter,
1174  GtkTreeIter *child)
1175 {
1176  GncTreeModelAccount *model;
1177  Account *account, *parent;
1178  gint i;
1179 
1180  if (child)
1181  {
1182  gchar *child_string;
1183 
1184  child_string = g_strdup (iter_to_string (child));
1185  ENTER("model %p, iter %s, child %s",
1186  tree_model, iter_to_string (iter),
1187  child_string);
1188  g_free (child_string);
1189  }
1190  else
1191  {
1192  ENTER("model %p, iter %s, child (null)",
1193  tree_model, iter_to_string (iter));
1194  }
1195  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1196 
1197  model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1198 
1199  gnc_leave_return_val_if_fail (child != NULL, FALSE);
1200  gnc_leave_return_val_if_fail (child->user_data != NULL, FALSE);
1201  gnc_leave_return_val_if_fail (child->stamp == model->stamp, FALSE);
1202 
1203  account = (Account *) child->user_data;
1204  account = gnc_account_get_parent (account);
1205  if (account == NULL)
1206  {
1207  /* Can't go up from the root node */
1208  iter->stamp = 0;
1209  LEAVE("failed (1)");
1210  return FALSE;
1211  }
1212 
1213  parent = gnc_account_get_parent (account);
1214  if (parent == NULL)
1215  {
1216  /* Now at the root. */
1217  i = 0;
1218  }
1219  else
1220  {
1221  i = gnc_account_child_index (parent, account);
1222  }
1223  iter->user_data = account;
1224  iter->user_data2 = parent;
1225  iter->user_data3 = GINT_TO_POINTER(i);
1226  iter->stamp = model->stamp;
1227  LEAVE("iter (2) %s", iter_to_string (iter));
1228  return TRUE;
1229 }
1230 
1231 
1232 /************************************************************/
1233 /* Account Tree View Filter Functions */
1234 /************************************************************/
1235 
1236 /*
1237  * Convert a model/iter pair to a gnucash account. This routine should
1238  * only be called from an account tree view filter function.
1239  */
1240 Account *
1241 gnc_tree_model_account_get_account (GncTreeModelAccount *model,
1242  GtkTreeIter *iter)
1243 {
1244  g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
1245  g_return_val_if_fail (iter != NULL, NULL);
1246  g_return_val_if_fail (iter->user_data != NULL, NULL);
1247  g_return_val_if_fail (iter->stamp == model->stamp, NULL);
1248 
1249  return (Account *) iter->user_data;
1250 }
1251 
1252 /*
1253  * Convert a model/account pair into a gtk_tree_model_iter. This
1254  * routine should only be called from the file
1255  * gnc-tree-view-account.c.
1256  */
1257 gboolean
1259  Account *account,
1260  GtkTreeIter *iter)
1261 {
1262  Account *parent;
1263  gint i;
1264 
1265  ENTER("model %p, account %p, iter %p", model, account, iter);
1266  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), FALSE);
1267  gnc_leave_return_val_if_fail ((account != NULL), FALSE);
1268  gnc_leave_return_val_if_fail ((iter != NULL), FALSE);
1269 
1270  iter->user_data = account;
1271  iter->stamp = model->stamp;
1272 
1273  if (account == model->root)
1274  {
1275  iter->user_data2 = NULL;
1276  iter->user_data3 = GINT_TO_POINTER(0);
1277  LEAVE("Matched root");
1278  return TRUE;
1279  }
1280 
1281  if (model->root != gnc_account_get_root (account))
1282  {
1283  LEAVE("Root doesn't match");
1284  return FALSE;
1285  }
1286 
1287  parent = gnc_account_get_parent (account);
1288  i = gnc_account_child_index (parent, account);
1289  iter->user_data2 = parent;
1290  iter->user_data3 = GINT_TO_POINTER(i);
1291  LEAVE("iter %s", iter_to_string (iter));
1292  return (i != -1);
1293 }
1294 
1295 /*
1296  * Convert a model/account pair into a gtk_tree_model_path. This
1297  * routine should only be called from the file
1298  * gnc-tree-view-account.c.
1299  */
1300 GtkTreePath *
1302  Account *account)
1303 {
1304  GtkTreeIter tree_iter;
1305  GtkTreePath *tree_path;
1306 
1307  ENTER("model %p, account %p", model, account);
1308  gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
1309  gnc_leave_return_val_if_fail (account != NULL, NULL);
1310 
1311  if (!gnc_tree_model_account_get_iter_from_account (model, account,
1312  &tree_iter))
1313  {
1314  LEAVE("no iter");
1315  return NULL;
1316  }
1317 
1318  tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
1319  if (tree_path)
1320  {
1321  gchar *path_string = gtk_tree_path_to_string (tree_path);
1322  LEAVE("path (2) %s", path_string);
1323  g_free (path_string);
1324  }
1325  else
1326  {
1327  LEAVE("no path");
1328  }
1329  return tree_path;
1330 }
1331 
1332 /************************************************************/
1333 /* Account Tree Model - Engine Event Handling Functions */
1334 /************************************************************/
1335 
1336 static void
1337 increment_stamp (GncTreeModelAccount *model)
1338 {
1339  do model->stamp++;
1340  while (!model->stamp);
1341 }
1342 
1343 static void
1344 propagate_change (GtkTreeModel *model, GtkTreePath *path, gint toggle_if_num)
1345 {
1346  GtkTreeIter iter;
1347 
1348  /* Already at the invisible root node? */
1349  if (!gtk_tree_path_up (path))
1350  return;
1351 
1352  /* Did we just move up to the invisible root node? */
1353  if (gtk_tree_path_get_depth (path) == 0)
1354  return;
1355 
1356  /* Handle the immediate parent */
1357  if (gtk_tree_model_get_iter (model, &iter, path))
1358  {
1359  gtk_tree_model_row_changed (model, path, &iter);
1360  if (gtk_tree_model_iter_n_children (model, &iter) == toggle_if_num)
1361  gtk_tree_model_row_has_child_toggled (model, path, &iter);
1362  }
1363 
1364  /* All other ancestors */
1365  while (gtk_tree_path_up (path) && gtk_tree_path_get_depth (path) > 0 &&
1366  gtk_tree_model_get_iter (model, &iter, path))
1367  {
1368  gtk_tree_model_row_changed (model, path, &iter);
1369  }
1370 }
1371 
1400 static void
1401 gnc_tree_model_account_event_handler (QofInstance *entity,
1402  QofEventId event_type,
1403  GncTreeModelAccount *model,
1404  GncEventData *ed)
1405 {
1406  const gchar *parent_name;
1407  GtkTreePath *path = NULL;
1408  GtkTreeIter iter;
1409  Account *account, *parent;
1410 
1411  g_return_if_fail (model); /* Required */
1412 
1413  if (!GNC_IS_ACCOUNT(entity))
1414  return;
1415 
1416  ENTER("entity %p of type %d, model %p, event_data %p",
1417  entity, event_type, model, ed);
1418 
1419  account = GNC_ACCOUNT(entity);
1420 
1421  if (gnc_account_get_book (account) != model->book)
1422  {
1423  LEAVE("not in this book");
1424  return;
1425  }
1426  if (gnc_account_get_root (account) != model->root)
1427  {
1428  LEAVE("not in this model");
1429  return;
1430  }
1431 
1432  /* clear the cached model values for account */
1433  if (event_type != QOF_EVENT_ADD)
1434  gnc_tree_model_account_clear_cached_values (model, account);
1435 
1436  /* What to do, that to do. */
1437  switch (event_type)
1438  {
1439  case QOF_EVENT_ADD:
1440  /* Tell the filters/views where the new account was added. */
1441  DEBUG("add account %p (%s)", account, xaccAccountGetName (account));
1442  path = gnc_tree_model_account_get_path_from_account (model, account);
1443  if (!path)
1444  {
1445  DEBUG("can't generate path");
1446  break;
1447  }
1448  increment_stamp (model);
1449  if (!gnc_tree_model_account_get_iter (GTK_TREE_MODEL(model), &iter, path))
1450  {
1451  DEBUG("can't generate iter");
1452  break;
1453  }
1454  gtk_tree_model_row_inserted (GTK_TREE_MODEL(model), path, &iter);
1455  propagate_change (GTK_TREE_MODEL(model), path, 1);
1456  break;
1457 
1458  case QOF_EVENT_REMOVE:
1459  if (!ed) /* Required for a remove. */
1460  break;
1461  parent = ed->node ? GNC_ACCOUNT(ed->node) : model->root;
1462  parent_name = ed->node ? xaccAccountGetName (parent) : "Root";
1463  DEBUG("remove child %d of account %p (%s)", ed->idx, parent, parent_name);
1464  path = gnc_tree_model_account_get_path_from_account (model, parent);
1465  if (!path)
1466  {
1467  DEBUG("can't generate path");
1468  break;
1469  }
1470  increment_stamp (model);
1471  gtk_tree_path_append_index (path, ed->idx);
1472  gtk_tree_model_row_deleted (GTK_TREE_MODEL(model), path);
1473  propagate_change (GTK_TREE_MODEL(model), path, 0);
1474  break;
1475 
1476  case QOF_EVENT_MODIFY:
1477  DEBUG("modify account %p (%s)", account, xaccAccountGetName (account));
1478  path = gnc_tree_model_account_get_path_from_account (model, account);
1479  if (!path)
1480  {
1481  DEBUG("can't generate path");
1482  break;
1483  }
1484  if (!gnc_tree_model_account_get_iter (GTK_TREE_MODEL(model), &iter, path))
1485  {
1486  DEBUG("can't generate iter");
1487  break;
1488  }
1489  gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
1490  propagate_change (GTK_TREE_MODEL(model), path, -1);
1491  break;
1492 
1493  default:
1494  LEAVE("unknown event type");
1495  return;
1496  }
1497 
1498  if (path)
1499  gtk_tree_path_free (path);
1500  LEAVE(" ");
1501  return;
1502 }
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2875
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition: Account.cpp:4859
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.c:128
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:3226
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:173
#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:248
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
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:2922
All type declarations for the whole Gnucash engine.
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition: Account.cpp:4653
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition: Account.cpp:4277
Generic api to store and retrieve preferences.
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition: Account.cpp:4217
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3411
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:4193
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:2938
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:2929
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:2882
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3248
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition: Account.cpp:4444
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.c:143
const char * xaccAccountGetNotes(const Account *acc)
Get the account&#39;s notes.
Definition: Account.cpp:3357