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