GnuCash c935c2f+
Loading...
Searching...
No Matches
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
33#include "gnc-component-manager.h"
34#include "Account.h"
35#include "dialog-utils.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
49static QofLogModule log_module = GNC_MOD_GUI;
50
52static void gnc_tree_model_account_finalize (GObject *object);
53static void gnc_tree_model_account_dispose (GObject *object);
54
56static void gnc_tree_model_account_tree_model_init (GtkTreeModelIface *iface);
57static GtkTreeModelFlags gnc_tree_model_account_get_flags (GtkTreeModel *tree_model);
58static int gnc_tree_model_account_get_n_columns (GtkTreeModel *tree_model);
59static GType gnc_tree_model_account_get_column_type (GtkTreeModel *tree_model,
60 int index);
61static gboolean gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
62 GtkTreeIter *iter,
63 GtkTreePath *path);
64static GtkTreePath *gnc_tree_model_account_get_path (GtkTreeModel *tree_model,
65 GtkTreeIter *iter);
66static void gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
67 GtkTreeIter *iter,
68 int column,
69 GValue *value);
70static gboolean gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
71 GtkTreeIter *iter);
72static gboolean gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
73 GtkTreeIter *iter,
74 GtkTreeIter *parent);
75static gboolean gnc_tree_model_account_iter_has_child (GtkTreeModel *tree_model,
76 GtkTreeIter *iter);
77static int gnc_tree_model_account_iter_n_children (GtkTreeModel *tree_model,
78 GtkTreeIter *iter);
79static gboolean gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
80 GtkTreeIter *iter,
81 GtkTreeIter *parent,
82 int n);
83static gboolean gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
84 GtkTreeIter *iter,
85 GtkTreeIter *child);
86
88static 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
108G_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
124static void
125gnc_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
152static void
153gnc_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
164static void
165gnc_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
199static void
200gnc_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
217static void
218gnc_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
255GtkTreeModel *
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
293static const gchar *
294iter_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
326static void
327gnc_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
343static GtkTreeModelFlags
344gnc_tree_model_account_get_flags (GtkTreeModel *tree_model)
345{
346 return 0;
347}
348
349static int
350gnc_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
357static GType
358gnc_tree_model_account_get_column_type (GtkTreeModel *tree_model, int index)
359{
360 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), G_TYPE_INVALID);
361 g_return_val_if_fail ((index < GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS) && (index >= 0), G_TYPE_INVALID);
362
363 switch (index)
364 {
365 case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
366 case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
367 case GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY:
368 case GNC_TREE_MODEL_ACCOUNT_COL_CODE:
369 case GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION:
370 case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT:
371 case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT:
372 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
373 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
374 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
375 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
376 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT_EXPLANATION:
377 case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
378 case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
379 case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
380 case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT:
381 case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE:
382 case GNC_TREE_MODEL_ACCOUNT_COL_EARLIEST_DATE:
383 case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN:
384 case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT:
385 case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL:
386 case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT:
387 case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD:
388 case GNC_TREE_MODEL_ACCOUNT_COL_NOTES:
389 case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO:
390 case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT:
391 case GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM:
392
393 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT:
394 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT:
395 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE:
396 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD:
397 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED:
398 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED:
399 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN:
400 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL:
401 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD:
402 return G_TYPE_STRING;
403
404 case GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN:
405 case GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER:
406 case GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE:
407 return G_TYPE_BOOLEAN;
408
409 default:
410 g_assert_not_reached ();
411 return G_TYPE_INVALID;
412 }
413}
414
415static gboolean
416gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
417 GtkTreeIter *iter,
418 GtkTreePath *path)
419{
420 GncTreeModelAccount *model;
421 Account *account, *parent;
422 gint i, *indices;
423
424 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
425
426 {
427 gchar *path_string = gtk_tree_path_to_string (path);
428 ENTER("model %p, iter %p, path %s", tree_model, iter, path_string);
429 g_free (path_string);
430 }
431
432 model = GNC_TREE_MODEL_ACCOUNT(tree_model);
433
434 if (gtk_tree_path_get_depth (path) <= 0)
435 {
436 LEAVE("bad depth");
437 return FALSE;
438 }
439
440 indices = gtk_tree_path_get_indices (path);
441 if (indices[0] != 0)
442 {
443 LEAVE("bad root index");
444 return FALSE;
445 }
446
447 parent = NULL;
448 account = model->root;
449 for (i = 1; i < gtk_tree_path_get_depth (path); i++)
450 {
451 parent = account;
452 account = gnc_account_nth_child (parent, indices[i]);
453 if (account == NULL)
454 {
455 iter->stamp = 0;
456 LEAVE("bad index");
457 return FALSE;
458 }
459 }
460
461 iter->stamp = model->stamp;
462 iter->user_data = account;
463 iter->user_data2 = parent;
464 iter->user_data3 = GINT_TO_POINTER(indices[i - 1]);
465
466 LEAVE("iter %s", iter_to_string (iter));
467 return TRUE;
468}
469
470static GtkTreePath *
471gnc_tree_model_account_get_path (GtkTreeModel *tree_model,
472 GtkTreeIter *iter)
473{
474 GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
475 Account *account, *parent;
476 GtkTreePath *path;
477 gint i;
478
479 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
480 g_return_val_if_fail (iter != NULL, NULL);
481 g_return_val_if_fail (iter->user_data != NULL, NULL);
482 g_return_val_if_fail (iter->stamp == model->stamp, NULL);
483
484 ENTER("model %p, iter %s", model, iter_to_string (iter));
485
486 if (model->root == NULL)
487 {
488 LEAVE("failed (1)");
489 return NULL;
490 }
491
492 account = (Account *) iter->user_data;
493 parent = (Account *) iter->user_data2;
494
495 path = gtk_tree_path_new ();
496 while (parent)
497 {
498 i = gnc_account_child_index (parent, account);
499 if (i == -1)
500 {
501 gtk_tree_path_free (path);
502 LEAVE("failed (3)");
503 return NULL;
504 }
505 gtk_tree_path_prepend_index (path, i);
506 account = parent;
507 parent = gnc_account_get_parent (account);
508 };
509
510 /* Add the root node. */
511 gtk_tree_path_prepend_index (path, 0);
512
513 {
514 gchar *path_string = gtk_tree_path_to_string (path);
515 LEAVE("path (4) %s", path_string);
516 g_free (path_string);
517 }
518 return path;
519}
520
521static void
522gnc_tree_model_account_set_color (GncTreeModelAccount *model,
523 gboolean negative,
524 GValue *value)
525{
526 if (negative)
527 g_value_set_static_string (value, model->negative_color);
528 else
529 g_value_set_static_string (value, NULL);
530}
531
532static gchar *
533gnc_tree_model_account_compute_period_balance (GncTreeModelAccount *model,
534 Account *acct,
535 gboolean recurse,
536 gboolean *negative)
537{
538 GNCPrintAmountInfo print_info;
539 time64 t1, t2;
540 gnc_numeric b3;
541
542 if (negative)
543 *negative = FALSE;
544
545 if (acct == model->root)
546 return g_strdup ("");
547
548 t1 = gnc_accounting_period_fiscal_start ();
549 t2 = gnc_accounting_period_fiscal_end ();
550
551 if (t1 > t2)
552 return g_strdup ("");
553
554 b3 = xaccAccountGetBalanceChangeForPeriod (acct, t1, t2, recurse);
555 if (gnc_reverse_balance (acct))
556 b3 = gnc_numeric_neg (b3);
557
558 if (negative)
559 *negative = gnc_numeric_negative_p (b3);
560
561 print_info = gnc_account_print_info (acct, TRUE);
562
563 return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (b3, print_info));
564}
565
566static gboolean
567row_changed_foreach_func (GtkTreeModel *model, GtkTreePath *path,
568 GtkTreeIter *iter, gpointer user_data)
569{
570 gtk_tree_model_row_changed (model, path, iter);
571 return FALSE;
572}
573
574void
575gnc_tree_model_account_clear_cache (GncTreeModelAccount *model)
576{
577 if (model)
578 {
579 // destroy the cached account values and recreate
580 g_hash_table_destroy (model->account_values_hash);
581 model->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
582 g_free, g_free);
583
584 gtk_tree_model_foreach (GTK_TREE_MODEL(model), row_changed_foreach_func, NULL);
585 }
586}
587
588static void
589clear_account_cached_values (GncTreeModelAccount *model, GHashTable *hash, Account *account)
590{
591 GtkTreeIter iter;
592 gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
593
594 if (!account)
595 return;
596
597 // make sure tree view sees the change
598 if (gnc_tree_model_account_get_iter_from_account (model, account, &iter))
599 {
600 GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
601
602 gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
603 gtk_tree_path_free (path);
604 }
605
606 guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
607
608 // loop over the columns and remove any found
609 for (gint col = 0; col <= GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS; col++)
610 {
611 gchar *key = g_strdup_printf ("%s,%d", acct_guid_str, col);
612
613 g_hash_table_remove (hash, key);
614 g_free (key);
615 }
616}
617
618static void
619gnc_tree_model_account_clear_cached_values (GncTreeModelAccount *model, Account *account)
620{
621 Account *parent;
622
623 // no hash table or account, return
624 if ((!model->account_values_hash) || (!account))
625 return;
626
627 clear_account_cached_values (model, model->account_values_hash, account);
628 parent = gnc_account_get_parent (account);
629
630 // clear also all parent accounts, this will update any balances/totals
631 while (parent)
632 {
633 clear_account_cached_values (model, model->account_values_hash, parent);
634 parent = gnc_account_get_parent (parent);
635 }
636}
637
638static gboolean
639gnc_tree_model_account_get_cached_value (GncTreeModelAccount *model, Account *account,
640 gint column, gchar **cached_string)
641{
642 gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
643 gchar *key = NULL;
644 gpointer value;
645 gboolean found;
646
647 if ((!model->account_values_hash) || (!account))
648 return FALSE;
649
650 guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
651 key = g_strdup_printf ("%s,%d", acct_guid_str, column);
652
653 found = g_hash_table_lookup_extended (model->account_values_hash, key,
654 NULL, &value);
655
656 if (found)
657 *cached_string = g_strdup (value);
658
659 g_free (key);
660
661 return found;
662}
663
664static void
665gnc_tree_model_account_set_cached_value (GncTreeModelAccount *model, Account *account,
666 gint column, GValue *value)
667{
668 if ((!model->account_values_hash) || (!account))
669 return;
670
671 // only interested in string values
672 if (G_VALUE_HOLDS_STRING(value))
673 {
674 gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
675 const gchar *str = g_value_get_string (value);
676 gchar *key = NULL;
677
678 guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
679 key = g_strdup_printf ("%s,%d", acct_guid_str, column);
680
681 g_hash_table_insert (model->account_values_hash, key, g_strdup (str));
682 }
683}
684
685static void
686gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
687 GtkTreeIter *iter,
688 int column,
689 GValue *value)
690{
691 GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
692 Account *account;
693 gboolean negative; /* used to set "deficit style" also known as red numbers */
694 gchar *string;
695 gchar *cached_string = NULL;
696
697 time64 last_date;
698
699 g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model));
700 g_return_if_fail (iter != NULL);
701 g_return_if_fail (iter->user_data != NULL);
702 g_return_if_fail (iter->stamp == model->stamp);
703
704 ENTER("model %p, iter %s, col %d", tree_model,
705 iter_to_string (iter), column);
706
707 account = (Account *) iter->user_data;
708
709 // lets see if the value is in the cache
710 if (gnc_tree_model_account_get_cached_value (model, account, column, &cached_string))
711 {
712 g_value_init (value, G_TYPE_STRING);
713 g_value_take_string (value, cached_string);
714 LEAVE("value in cache, '%s'", cached_string);
715 return;
716 }
717
718 switch (column)
719 {
720 case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
721 g_value_init (value, G_TYPE_STRING);
722 if (account == model->root)
723 g_value_set_string (value, _("New top level account"));
724 else
725 g_value_set_string (value, xaccAccountGetName (account));
726 break;
727 case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
728 g_value_init (value, G_TYPE_STRING);
729 g_value_set_string (value,
731 break;
732 case GNC_TREE_MODEL_ACCOUNT_COL_CODE:
733 g_value_init (value, G_TYPE_STRING);
734 g_value_set_string (value, xaccAccountGetCode (account));
735 break;
736 case GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY:
737 g_value_init (value, G_TYPE_STRING);
738 g_value_set_string (value,
740 break;
741 case GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION:
742 g_value_init (value, G_TYPE_STRING);
743 g_value_set_string (value, xaccAccountGetDescription (account));
744 break;
745
746 case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT:
747 g_value_init (value, G_TYPE_STRING);
748 string = gnc_ui_account_get_print_balance (xaccAccountGetPresentBalanceInCurrency,
749 account, TRUE, &negative);
750 g_value_take_string (value, string);
751 break;
752 case GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT:
753 g_value_init (value, G_TYPE_STRING);
754 string = gnc_ui_account_get_print_report_balance (xaccAccountGetPresentBalanceInCurrency,
755 account, TRUE, &negative);
756 g_value_take_string (value, string);
757 break;
758 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT:
759 g_value_init (value, G_TYPE_STRING);
760 string = gnc_ui_account_get_print_balance (xaccAccountGetPresentBalanceInCurrency,
761 account, TRUE, &negative);
762 gnc_tree_model_account_set_color (model, negative, value);
763 g_free (string);
764 break;
765
766 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
767 g_value_init (value, G_TYPE_STRING);
768 string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
769 account, FALSE, &negative);
770 g_value_take_string (value, string);
771 break;
772 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
773 g_value_init (value, G_TYPE_STRING);
774 string = gnc_ui_account_get_print_report_balance (xaccAccountGetBalanceInCurrency,
775 account, FALSE, &negative);
776 g_value_take_string (value, string);
777 break;
778 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE:
779 g_value_init (value, G_TYPE_STRING);
780 string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
781 account, FALSE, &negative);
782 gnc_tree_model_account_set_color (model, negative, value);
783 g_free (string);
784 break;
785 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
786 g_value_init (value, G_TYPE_STRING);
787 string = gnc_tree_model_account_compute_period_balance (model, account, FALSE, &negative);
788 g_value_take_string (value, string);
789 break;
790 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD:
791 g_value_init (value, G_TYPE_STRING);
792 string = gnc_tree_model_account_compute_period_balance (model, account, FALSE, &negative);
793 gnc_tree_model_account_set_color (model, negative, value);
794 g_free (string);
795 break;
796
797 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
798 g_value_init (value, G_TYPE_STRING);
799 string = gnc_ui_account_get_balance_limit_icon_name (account);
800 g_value_take_string (value, string);
801 break;
802
803 case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT_EXPLANATION:
804 g_value_init (value, G_TYPE_STRING);
805 string = gnc_ui_account_get_balance_limit_explanation (account);
806 g_value_take_string (value, string);
807 break;
808
809 case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
810 g_value_init (value, G_TYPE_STRING);
811 string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,
812 account, TRUE, &negative);
813 g_value_take_string (value, string);
814 break;
815 case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
816 g_value_init (value, G_TYPE_STRING);
817 string = gnc_ui_account_get_print_report_balance (xaccAccountGetClearedBalanceInCurrency,
818 account, TRUE, &negative);
819 g_value_take_string (value, string);
820 break;
821 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED:
822 g_value_init (value, G_TYPE_STRING);
823 string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,
824 account, TRUE, &negative);
825 gnc_tree_model_account_set_color (model, negative, value);
826 g_free (string);
827 break;
828
829 case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
830 g_value_init (value, G_TYPE_STRING);
831 string = gnc_ui_account_get_print_balance (xaccAccountGetReconciledBalanceInCurrency,
832 account, TRUE, &negative);
833 g_value_take_string (value, string);
834 break;
835 case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT:
836 g_value_init (value, G_TYPE_STRING);
837 string = gnc_ui_account_get_print_report_balance (xaccAccountGetReconciledBalanceInCurrency,
838 account, TRUE, &negative);
839 g_value_take_string (value, string);
840 break;
841 case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE:
842 g_value_init (value, G_TYPE_STRING);
843 if (xaccAccountGetReconcileLastDate (account, &last_date))
844 g_value_take_string (value, qof_print_date (last_date));
845 break;
846
847 case GNC_TREE_MODEL_ACCOUNT_COL_EARLIEST_DATE:
848 g_value_init (value, G_TYPE_STRING);
849 time64 earliest = gnc_account_get_earliest_date (account);
850 if (earliest != INT64_MAX)
851 g_value_take_string (value, qof_print_date (earliest));
852 break;
853
854 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED:
855 g_value_init (value, G_TYPE_STRING);
856 string = gnc_ui_account_get_print_balance (xaccAccountGetReconciledBalanceInCurrency,
857 account, TRUE, &negative);
858 gnc_tree_model_account_set_color (model, negative, value);
859 g_free (string);
860 break;
861
862 case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN:
863 g_value_init (value, G_TYPE_STRING);
864 string = gnc_ui_account_get_print_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
865 account, TRUE, &negative);
866 g_value_take_string (value, string);
867 break;
868 case GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT:
869 g_value_init (value, G_TYPE_STRING);
870 string = gnc_ui_account_get_print_report_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
871 account, TRUE, &negative);
872 g_value_take_string (value, string);
873 break;
874 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN:
875 g_value_init (value, G_TYPE_STRING);
876 string = gnc_ui_account_get_print_balance (xaccAccountGetProjectedMinimumBalanceInCurrency,
877 account, TRUE, &negative);
878 gnc_tree_model_account_set_color (model, negative, value);
879 g_free (string);
880 break;
881
882 case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL:
883 g_value_init (value, G_TYPE_STRING);
884 string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
885 account, TRUE, &negative);
886 g_value_take_string (value, string);
887 break;
888 case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT:
889 g_value_init (value, G_TYPE_STRING);
890 string = gnc_ui_account_get_print_report_balance (xaccAccountGetBalanceInCurrency,
891 account, TRUE, &negative);
892 g_value_take_string (value, string);
893 break;
894 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL:
895 g_value_init (value, G_TYPE_STRING);
896 string = gnc_ui_account_get_print_balance (xaccAccountGetBalanceInCurrency,
897 account, TRUE, &negative);
898 gnc_tree_model_account_set_color (model, negative, value);
899 g_free (string);
900 break;
901 case GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD:
902 g_value_init (value, G_TYPE_STRING);
903 string = gnc_tree_model_account_compute_period_balance (model, account, TRUE, &negative);
904 g_value_take_string (value, string);
905 break;
906 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD:
907 g_value_init (value, G_TYPE_STRING);
908 string = gnc_tree_model_account_compute_period_balance (model, account, TRUE, &negative);
909 gnc_tree_model_account_set_color (model, negative, value);
910 g_free (string);
911 break;
912
913 case GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT:
914 g_value_init (value, G_TYPE_STRING);
915 g_value_set_string (value, xaccAccountGetColor (account));
916 break;
917
918 case GNC_TREE_MODEL_ACCOUNT_COL_NOTES:
919 g_value_init (value, G_TYPE_STRING);
920 g_value_set_string (value, xaccAccountGetNotes (account));
921 break;
922
923 case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO:
924 g_value_init (value, G_TYPE_STRING);
925 g_value_take_string (value, gnc_ui_account_get_tax_info_string (account));
926 break;
927
928 case GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT:
929 g_value_init (value, G_TYPE_STRING);
930 g_value_take_string (value, gnc_ui_account_get_tax_info_sub_acct_string (account));
931 break;
932
933 case GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM:
934 g_value_init (value, G_TYPE_STRING);
935 g_value_set_string (value, xaccAccountGetLastNum (account));
936 break;
937
938 case GNC_TREE_MODEL_ACCOUNT_COL_HIDDEN:
939 g_value_init (value, G_TYPE_BOOLEAN);
940 g_value_set_boolean (value, xaccAccountGetHidden (account));
941 break;
942
943 case GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER:
944 g_value_init (value, G_TYPE_BOOLEAN);
945 g_value_set_boolean (value, xaccAccountGetPlaceholder (account));
946 break;
947
948 case GNC_TREE_MODEL_ACCOUNT_COL_OPENING_BALANCE:
949 g_value_init (value, G_TYPE_BOOLEAN);
950 g_value_set_boolean (value, xaccAccountGetIsOpeningBalance (account));
951 break;
952
953 default:
954 g_assert_not_reached ();
955 break;
956 }
957
958 // save the value to the account values cache
959 gnc_tree_model_account_set_cached_value (model, account, column, value);
960
961 LEAVE(" ");
962}
963
964static gboolean
965gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
966 GtkTreeIter *iter)
967{
968 GncTreeModelAccount *model = GNC_TREE_MODEL_ACCOUNT(tree_model);
969 Account *account, *parent;
970 gint i;
971
972 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), FALSE);
973 g_return_val_if_fail (iter != NULL, FALSE);
974 g_return_val_if_fail (iter->user_data != NULL, FALSE);
975 g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
976
977 ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
978
979 parent = (Account *) iter->user_data2;
980 if (parent == NULL)
981 {
982 /* This is the root. There is no next. */
983 LEAVE("at root");
984 return FALSE;
985 }
986
987 /* Get the *next* sibling account. */
988 i = GPOINTER_TO_INT(iter->user_data3);
989 account = gnc_account_nth_child (parent, i + 1);
990 if (account == NULL)
991 {
992 iter->stamp = 0;
993 LEAVE("failed (3)");
994 return FALSE;
995 }
996
997 iter->user_data = account;
998 iter->user_data2 = parent;
999 iter->user_data3 = GINT_TO_POINTER(i + 1);
1000
1001 LEAVE("iter %s", iter_to_string (iter));
1002 return TRUE;
1003}
1004
1005static gboolean
1006gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
1007 GtkTreeIter *iter,
1008 GtkTreeIter *parent_iter)
1009{
1010 GncTreeModelAccount *model;
1011 Account *account, *parent;
1012
1013 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1014 ENTER("model %p, iter %p (to be filed in), parent %s",
1015 tree_model, iter, (parent_iter ? iter_to_string (parent_iter) : "(null)"));
1016
1017 model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1018
1019 if (model->root == NULL)
1020 {
1021 iter->stamp = 0;
1022 LEAVE("failed (no root)");
1023 return FALSE;
1024 }
1025
1026 /* Special case when no parent supplied. */
1027 if (!parent_iter)
1028 {
1029 iter->user_data = model->root;
1030 iter->user_data2 = NULL;
1031 iter->user_data3 = GINT_TO_POINTER(0);
1032 iter->stamp = model->stamp;
1033 LEAVE("iter (2) %s", iter_to_string (iter));
1034 return TRUE;
1035 }
1036
1037 gnc_leave_return_val_if_fail (parent_iter != NULL, FALSE);
1038 gnc_leave_return_val_if_fail (parent_iter->user_data != NULL, FALSE);
1039 gnc_leave_return_val_if_fail (parent_iter->stamp == model->stamp, FALSE);
1040
1041 parent = (Account *)parent_iter->user_data;
1042 account = gnc_account_nth_child (parent, 0);
1043
1044 if (account == NULL)
1045 {
1046 iter->stamp = 0;
1047 LEAVE("failed (child account is null)");
1048 return FALSE;
1049 }
1050
1051 iter->user_data = account;
1052 iter->user_data2 = parent;
1053 iter->user_data3 = GINT_TO_POINTER(0);
1054 iter->stamp = model->stamp;
1055 LEAVE("iter (3) %s", iter_to_string (iter));
1056 return TRUE;
1057}
1058
1059static gboolean
1060gnc_tree_model_account_iter_has_child (GtkTreeModel *tree_model,
1061 GtkTreeIter *iter)
1062{
1063 GncTreeModelAccount *model;
1064 Account *account;
1065
1066 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1067
1068 ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
1069
1070 model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1071
1072 gnc_leave_return_val_if_fail (iter != NULL, FALSE);
1073 gnc_leave_return_val_if_fail (iter->user_data != NULL, FALSE);
1074 gnc_leave_return_val_if_fail (iter->stamp == model->stamp, FALSE);
1075
1076 account = (Account *) iter->user_data;
1077 if (gnc_account_n_children (account) > 0)
1078 {
1079 LEAVE("yes");
1080 return TRUE;
1081 }
1082
1083 LEAVE("no");
1084 return FALSE;
1085}
1086
1087static int
1088gnc_tree_model_account_iter_n_children (GtkTreeModel *tree_model,
1089 GtkTreeIter *iter)
1090{
1091 GncTreeModelAccount *model;
1092 gint num;
1093
1094 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1095 ENTER("model %p, iter %s", tree_model, iter_to_string (iter));
1096
1097 model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1098
1099 if (iter == NULL)
1100 {
1101 /* How many children does the invisible root node
1102 * have. One! Its the real root account node. */
1103 LEAVE("count is 1");
1104 return 1;
1105 }
1106
1107 gnc_leave_return_val_if_fail (iter != NULL, FALSE);
1108 gnc_leave_return_val_if_fail (iter->user_data != NULL, FALSE);
1109 gnc_leave_return_val_if_fail (iter->stamp == model->stamp, FALSE);
1110
1111 num = gnc_account_n_children (iter->user_data);
1112 LEAVE("count is %d", num);
1113 return num;
1114}
1115
1116static gboolean
1117gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
1118 GtkTreeIter *iter,
1119 GtkTreeIter *parent_iter,
1120 int n)
1121{
1122 GncTreeModelAccount *model;
1123 Account *account, *parent;
1124
1125 if (parent_iter)
1126 {
1127 gchar *parent_string;
1128 parent_string = g_strdup (iter_to_string (parent_iter));
1129 ENTER("model %p, iter %s, parent_iter %s, n %d",
1130 tree_model, iter_to_string (iter),
1131 parent_string, n);
1132 g_free (parent_string);
1133 }
1134 else
1135 {
1136 ENTER("model %p, iter %s, parent_iter (null), n %d",
1137 tree_model, iter_to_string (iter), n);
1138 }
1139 gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1140
1141 model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1142
1143 /* Special case when no parent supplied. */
1144 if (!parent_iter)
1145 {
1146 if (n != 0)
1147 {
1148 LEAVE("bad root index");
1149 return FALSE;
1150 }
1151
1152 iter->user_data = model->root;
1153 iter->user_data2 = NULL;
1154 iter->user_data3 = GINT_TO_POINTER(0);
1155 iter->stamp = model->stamp;
1156 LEAVE("root %s", iter_to_string (iter));
1157 return TRUE;
1158 }
1159
1160 gnc_leave_return_val_if_fail (parent_iter->user_data != NULL, FALSE);
1161 gnc_leave_return_val_if_fail (parent_iter->stamp == model->stamp, FALSE);
1162
1163 parent = (Account *)parent_iter->user_data;
1164 account = gnc_account_nth_child (parent, n);
1165 if (account == NULL)
1166 {
1167 iter->stamp = 0;
1168 LEAVE("failed (2)");
1169 return FALSE;
1170 }
1171
1172 iter->user_data = account;
1173 iter->user_data2 = parent;
1174 iter->user_data3 = GINT_TO_POINTER(n);
1175 iter->stamp = model->stamp;
1176 LEAVE("iter (2) %s", iter_to_string (iter));
1177 return TRUE;
1178}
1179
1180static gboolean
1181gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
1182 GtkTreeIter *iter,
1183 GtkTreeIter *child)
1184{
1185 GncTreeModelAccount *model;
1186 Account *account, *parent;
1187 gint i;
1188
1189 if (child)
1190 {
1191 gchar *child_string;
1192
1193 child_string = g_strdup (iter_to_string (child));
1194 ENTER("model %p, iter %s, child %s",
1195 tree_model, iter_to_string (iter),
1196 child_string);
1197 g_free (child_string);
1198 }
1199 else
1200 {
1201 ENTER("model %p, iter %s, child (null)",
1202 tree_model, iter_to_string (iter));
1203 }
1204 gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(tree_model), FALSE);
1205
1206 model = GNC_TREE_MODEL_ACCOUNT(tree_model);
1207
1208 gnc_leave_return_val_if_fail (child != NULL, FALSE);
1209 gnc_leave_return_val_if_fail (child->user_data != NULL, FALSE);
1210 gnc_leave_return_val_if_fail (child->stamp == model->stamp, FALSE);
1211
1212 account = (Account *) child->user_data;
1213 account = gnc_account_get_parent (account);
1214 if (account == NULL)
1215 {
1216 /* Can't go up from the root node */
1217 iter->stamp = 0;
1218 LEAVE("failed (1)");
1219 return FALSE;
1220 }
1221
1222 parent = gnc_account_get_parent (account);
1223 if (parent == NULL)
1224 {
1225 /* Now at the root. */
1226 i = 0;
1227 }
1228 else
1229 {
1230 i = gnc_account_child_index (parent, account);
1231 }
1232 iter->user_data = account;
1233 iter->user_data2 = parent;
1234 iter->user_data3 = GINT_TO_POINTER(i);
1235 iter->stamp = model->stamp;
1236 LEAVE("iter (2) %s", iter_to_string (iter));
1237 return TRUE;
1238}
1239
1240
1241/************************************************************/
1242/* Account Tree View Filter Functions */
1243/************************************************************/
1244
1245/*
1246 * Convert a model/iter pair to a gnucash account. This routine should
1247 * only be called from an account tree view filter function.
1248 */
1249Account *
1250gnc_tree_model_account_get_account (GncTreeModelAccount *model,
1251 GtkTreeIter *iter)
1252{
1253 g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
1254 g_return_val_if_fail (iter != NULL, NULL);
1255 g_return_val_if_fail (iter->user_data != NULL, NULL);
1256 g_return_val_if_fail (iter->stamp == model->stamp, NULL);
1257
1258 return (Account *) iter->user_data;
1259}
1260
1261/*
1262 * Convert a model/account pair into a gtk_tree_model_iter. This
1263 * routine should only be called from the file
1264 * gnc-tree-view-account.c.
1265 */
1266gboolean
1268 Account *account,
1269 GtkTreeIter *iter)
1270{
1271 Account *parent;
1272 gint i;
1273
1274 ENTER("model %p, account %p, iter %p", model, account, iter);
1275 gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), FALSE);
1276 gnc_leave_return_val_if_fail ((account != NULL), FALSE);
1277 gnc_leave_return_val_if_fail ((iter != NULL), FALSE);
1278
1279 iter->user_data = account;
1280 iter->stamp = model->stamp;
1281
1282 if (account == model->root)
1283 {
1284 iter->user_data2 = NULL;
1285 iter->user_data3 = GINT_TO_POINTER(0);
1286 LEAVE("Matched root");
1287 return TRUE;
1288 }
1289
1290 if (model->root != gnc_account_get_root (account))
1291 {
1292 LEAVE("Root doesn't match");
1293 return FALSE;
1294 }
1295
1296 parent = gnc_account_get_parent (account);
1297 i = gnc_account_child_index (parent, account);
1298 iter->user_data2 = parent;
1299 iter->user_data3 = GINT_TO_POINTER(i);
1300 LEAVE("iter %s", iter_to_string (iter));
1301 return (i != -1);
1302}
1303
1304/*
1305 * Convert a model/account pair into a gtk_tree_model_path. This
1306 * routine should only be called from the file
1307 * gnc-tree-view-account.c.
1308 */
1309GtkTreePath *
1311 Account *account)
1312{
1313 GtkTreeIter tree_iter;
1314 GtkTreePath *tree_path;
1315
1316 ENTER("model %p, account %p", model, account);
1317 gnc_leave_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT(model), NULL);
1318 gnc_leave_return_val_if_fail (account != NULL, NULL);
1319
1321 &tree_iter))
1322 {
1323 LEAVE("no iter");
1324 return NULL;
1325 }
1326
1327 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
1328 if (tree_path)
1329 {
1330 gchar *path_string = gtk_tree_path_to_string (tree_path);
1331 LEAVE("path (2) %s", path_string);
1332 g_free (path_string);
1333 }
1334 else
1335 {
1336 LEAVE("no path");
1337 }
1338 return tree_path;
1339}
1340
1341/************************************************************/
1342/* Account Tree Model - Engine Event Handling Functions */
1343/************************************************************/
1344
1345static void
1346increment_stamp (GncTreeModelAccount *model)
1347{
1348 do model->stamp++;
1349 while (!model->stamp);
1350}
1351
1352static void
1353propagate_change (GtkTreeModel *model, GtkTreePath *path, gint toggle_if_num)
1354{
1355 GtkTreeIter iter;
1356
1357 /* Already at the invisible root node? */
1358 if (!gtk_tree_path_up (path))
1359 return;
1360
1361 /* Did we just move up to the invisible root node? */
1362 if (gtk_tree_path_get_depth (path) == 0)
1363 return;
1364
1365 /* Handle the immediate parent */
1366 if (gtk_tree_model_get_iter (model, &iter, path))
1367 {
1368 gtk_tree_model_row_changed (model, path, &iter);
1369 if (gtk_tree_model_iter_n_children (model, &iter) == toggle_if_num)
1370 gtk_tree_model_row_has_child_toggled (model, path, &iter);
1371 }
1372
1373 /* All other ancestors */
1374 while (gtk_tree_path_up (path) && gtk_tree_path_get_depth (path) > 0 &&
1375 gtk_tree_model_get_iter (model, &iter, path))
1376 {
1377 gtk_tree_model_row_changed (model, path, &iter);
1378 }
1379}
1380
1409static void
1410gnc_tree_model_account_event_handler (QofInstance *entity,
1411 QofEventId event_type,
1412 GncTreeModelAccount *model,
1413 GncEventData *ed)
1414{
1415 const gchar *parent_name;
1416 GtkTreePath *path = NULL;
1417 GtkTreeIter iter;
1418 Account *account, *parent;
1419
1420 g_return_if_fail (model); /* Required */
1421
1422 if (!GNC_IS_ACCOUNT(entity))
1423 return;
1424
1425 ENTER("entity %p of type %d, model %p, event_data %p",
1426 entity, event_type, model, ed);
1427
1428 account = GNC_ACCOUNT(entity);
1429
1430 if (gnc_account_get_book (account) != model->book)
1431 {
1432 LEAVE("not in this book");
1433 return;
1434 }
1435 if (gnc_account_get_root (account) != model->root)
1436 {
1437 LEAVE("not in this model");
1438 return;
1439 }
1440
1441 switch (event_type)
1442 {
1443 case QOF_EVENT_ADD:
1444 /* Tell the filters/views where the new account was added. */
1445 DEBUG("add account %p (%s)", account, xaccAccountGetName (account));
1446 path = gnc_tree_model_account_get_path_from_account (model, account);
1447 if (!path)
1448 {
1449 DEBUG("can't generate path");
1450 break;
1451 }
1452 increment_stamp (model);
1453 if (!gnc_tree_model_account_get_iter (GTK_TREE_MODEL(model), &iter, path))
1454 {
1455 DEBUG("can't generate iter");
1456 break;
1457 }
1458 gtk_tree_model_row_inserted (GTK_TREE_MODEL(model), path, &iter);
1459 propagate_change (GTK_TREE_MODEL(model), path, 1);
1460 break;
1461
1462 case QOF_EVENT_REMOVE:
1463 if (!ed) /* Required for a remove. */
1464 break;
1465 parent = ed->node ? GNC_ACCOUNT(ed->node) : model->root;
1466 parent_name = ed->node ? xaccAccountGetName (parent) : "Root";
1467 DEBUG("remove child %d of account %p (%s)", ed->idx, parent, parent_name);
1468 path = gnc_tree_model_account_get_path_from_account (model, parent);
1469 if (!path)
1470 {
1471 DEBUG("can't generate path");
1472 break;
1473 }
1474 increment_stamp (model);
1475 gtk_tree_path_append_index (path, ed->idx);
1476 gtk_tree_model_row_deleted (GTK_TREE_MODEL(model), path);
1477 propagate_change (GTK_TREE_MODEL(model), path, 0);
1478 break;
1479
1480 case QOF_EVENT_MODIFY:
1481 DEBUG("modify account %p (%s)", account, xaccAccountGetName (account));
1482 /* clear the cached model values for account */
1483 gnc_tree_model_account_clear_cached_values(model, account);
1484
1485 path = gnc_tree_model_account_get_path_from_account(model, account);
1486 if (!path)
1487 {
1488 DEBUG("can't generate path");
1489 break;
1490 }
1491 if (!gnc_tree_model_account_get_iter (GTK_TREE_MODEL(model), &iter, path))
1492 {
1493 DEBUG("can't generate iter");
1494 break;
1495 }
1496 gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
1497 propagate_change (GTK_TREE_MODEL(model), path, -1);
1498 break;
1499
1500 default:
1501 gnc_tree_model_account_clear_cached_values(model, account);
1502
1503 LEAVE("unknown event type");
1504 return;
1505 }
1506
1507 if (path)
1508 gtk_tree_path_free (path);
1509 LEAVE(" ");
1510 return;
1511}
Account handling public routines.
General utilities for dealing with accounting periods.
Commodity handling public routines.
All type declarations for the whole Gnucash engine.
Additional event handling code.
Gobject helper routines.
Generic api to store and retrieve preferences.
GtkTreeModel implementation for gnucash account tree.
utility functions for the GnuCash UI
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
const char * xaccAccountGetDescription(const Account *acc)
Get the account's description.
Definition Account.cpp:3343
const char * xaccAccountGetColor(const Account *acc)
Get the account's color.
Definition Account.cpp:3350
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:2942
gint gnc_account_child_index(const Account *parent, const Account *child)
Return the index of the specified child within the list of the parent's children.
Definition Account.cpp:2983
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
Account * gnc_account_nth_child(const Account *parent, gint num)
Return the n'th child account of the specified parent account.
Definition Account.cpp:2993
#define xaccAccountGetGUID(X)
Definition Account.h:252
const char * xaccAccountGetLastNum(const Account *acc)
Get the last num field of an Account.
Definition Account.cpp:4683
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition Account.cpp:2935
const char * xaccAccountGetNotes(const Account *acc)
Get the account's notes.
Definition Account.cpp:3374
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition Account.cpp:2976
const char * xaccAccountGetCode(const Account *acc)
Get the account's accounting code.
Definition Account.cpp:3336
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
char * qof_print_date(time64 t)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:610
gboolean xaccAccountGetHidden(const Account *acc)
Get the "hidden" flag for an account.
Definition Account.cpp:4190
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition Account.cpp:4119
gboolean xaccAccountGetIsOpeningBalance(const Account *acc)
Get the "opening-balance" flag for an account.
Definition Account.cpp:4143
time64 gnc_account_get_earliest_date(const Account *account)
Returns the date of the earliest split in the account, or INT64_MAX.
Definition Account.cpp:5127
gboolean xaccAccountGetReconcileLastDate(const Account *acc, time64 *last_date)
DOCUMENT ME!
Definition Account.cpp:4566
const char * xaccAccountGetTypeStr(GNCAccountType type)
The xaccAccountGetTypeStr() routine returns a string suitable for use in the GUI/Interface.
Definition Account.cpp:4357
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition qofevent.cpp:103
void(* QofEventHandler)(QofInstance *ent, QofEventId event_type, gpointer handler_data, gpointer event_data)
Handler invoked when an event is generated.
Definition qofevent.h:89
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition qofevent.cpp:73
gint QofEventId
Define the type of events allowed.
Definition qofevent.h:45
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
Account * gnc_tree_model_account_get_account(GncTreeModelAccount *model, GtkTreeIter *iter)
Convert a model/iter pair to a gnucash account.
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.
void gnc_tree_model_account_clear_cache(GncTreeModelAccount *model)
Clear the tree model account cached values.
GtkTreeModel * gnc_tree_model_account_new(Account *root)
Create a new GtkTreeModel for manipulating gnucash accounts.
GtkTreePath * gnc_tree_model_account_get_path_from_account(GncTreeModelAccount *model, Account *account)
Convert a model/account pair into a gtk_tree_model_path.
const char * gnc_print_amount_with_bidi_ltr_isolate(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
#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
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
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.
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.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
STRUCTS.
QofBook reference.
Definition qofbook-p.hpp:47
The instance data structure for an account tree model.
GncTreeModel gnc_tree_model
The parent object data.
int stamp
The state of the model.