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