GnuCash  5.6-150-g038405b370+
gnc-ledger-display.c
1 /********************************************************************\
2  * gnc-ledger-display.c -- utilities for dealing with multiple *
3  * register/ledger windows in GnuCash *
4  * *
5  * Copyright (C) 1997 Robin D. Clark *
6  * Copyright (C) 1997, 1998 Linas Vepstas *
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, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22 \********************************************************************/
23 
24 #include <config.h>
25 
26 #include <time.h>
27 
28 #include "Account.h"
29 #include "Query.h"
30 #include "qof.h"
31 #include "SX-book.h"
32 #include "Transaction.h"
33 #include "gnc-component-manager.h"
34 #include "gnc-date.h"
35 #include "gnc-engine.h"
36 #include "gnc-event.h"
37 #include "gnc-ledger-display.h"
38 #include "gnc-prefs.h"
39 #include "gnc-ui-util.h"
40 #include <gnc-glib-utils.h>
41 #include "split-register-control.h"
42 #include "split-register-model.h"
43 
44 
45 #define REGISTER_SINGLE_CM_CLASS "register-single"
46 #define REGISTER_SUBACCOUNT_CM_CLASS "register-subaccount"
47 #define REGISTER_GL_CM_CLASS "register-gl"
48 #define REGISTER_TEMPLATE_CM_CLASS "register-template"
49 
50 #define GNC_PREF_DOUBLE_LINE_MODE "double-line-mode"
51 #define GNC_PREF_MAX_TRANS "max-transactions"
52 #define GNC_PREF_DEFAULT_STYLE_LEDGER "default-style-ledger"
53 #define GNC_PREF_DEFAULT_STYLE_AUTOLEDGER "default-style-autoledger"
54 #define GNC_PREF_DEFAULT_STYLE_JOURNAL "default-style-journal"
55 
56 
58 {
59  GncGUID leader;
60 
61  Query* query;
62  Query* pre_filter_query;
63 
64  GNCLedgerDisplayType ld_type;
65 
66  SplitRegister* reg;
67 
68  gboolean loading;
69  gboolean use_double_line_default;
70  gboolean visible; /* focus */
71  gboolean needs_refresh;
72 
73  GNCLedgerDisplayDestroy destroy;
74  GNCLedgerDisplayGetParent get_parent;
75 
76  GHashTable *excluded_template_acc_hash;
77 
78  gpointer user_data;
79 
80  gint number_of_subaccounts;
81 
82  gint component_id;
83 };
84 
85 
87 static QofLogModule log_module = GNC_MOD_LEDGER;
88 
89 
91 static GNCLedgerDisplay*
92 gnc_ledger_display_internal (Account* lead_account, Query* q,
93  GNCLedgerDisplayType ld_type,
94  SplitRegisterType reg_type,
95  SplitRegisterStyle style,
96  gboolean use_double_line,
97  gboolean is_template,
98  gboolean mismatched_commodities);
99 
100 static void
101 gnc_ledger_display_refresh_internal (GNCLedgerDisplay* ld);
102 
103 static void gnc_ledger_display_make_query (GNCLedgerDisplay* ld,
104  gint limit,
105  SplitRegisterType type);
106 
109 Account*
110 gnc_ledger_display_leader (GNCLedgerDisplay* ld)
111 {
112  if (!ld)
113  return NULL;
114 
115  return xaccAccountLookup (&ld->leader, gnc_get_current_book());
116 }
117 
118 GNCLedgerDisplayType
119 gnc_ledger_display_type (GNCLedgerDisplay* ld)
120 {
121  if (!ld)
122  return -1;
123 
124  return ld->ld_type;
125 }
126 
127 void
128 gnc_ledger_display_set_user_data (GNCLedgerDisplay* ld, gpointer user_data)
129 {
130  if (!ld)
131  return;
132 
133  ld->user_data = user_data;
134 }
135 
136 gpointer
137 gnc_ledger_display_get_user_data (GNCLedgerDisplay* ld)
138 {
139  if (!ld)
140  return NULL;
141 
142  return ld->user_data;
143 }
144 
145 void
146 gnc_ledger_display_set_handlers (GNCLedgerDisplay* ld,
147  GNCLedgerDisplayDestroy destroy,
148  GNCLedgerDisplayGetParent get_parent)
149 {
150  if (!ld)
151  return;
152 
153  ld->destroy = destroy;
154  ld->get_parent = get_parent;
155 }
156 
157 SplitRegister*
159 {
160  if (!ld)
161  return NULL;
162 
163  return ld->reg;
164 }
165 
166 Query*
167 gnc_ledger_display_get_query (GNCLedgerDisplay* ld)
168 {
169  if (!ld)
170  return NULL;
171 
172  return ld->query;
173 }
174 
175 static void
176 exclude_template_accounts (Query* q, GHashTable *excluded_template_acc_hash)
177 {
178  Account* tRoot;
179  GList* al;
180 
181  tRoot = gnc_book_get_template_root (gnc_get_current_book());
182  al = gnc_account_get_descendants (tRoot);
183 
184  if (gnc_list_length_cmp (al, 0) && excluded_template_acc_hash)
185  {
186  GList *node, *next;
187 
188  for (node = al; node; node = next)
189  {
190  Account *acc = node->data;
191  next = g_list_next (node);
192 
193  if (g_hash_table_lookup (excluded_template_acc_hash, acc) != NULL)
194  al = g_list_delete_link (al, node);
195  else
196  g_hash_table_insert (excluded_template_acc_hash, acc, acc);
197  }
198  }
199  if (gnc_list_length_cmp (al, 0))
200  xaccQueryAddAccountMatch (q, al, QOF_GUID_MATCH_NONE, QOF_QUERY_AND);
201 
202  g_list_free (al);
203  al = NULL;
204  tRoot = NULL;
205 }
206 
207 static gboolean
208 find_by_leader (gpointer find_data, gpointer user_data)
209 {
210  Account* account = find_data;
211  GNCLedgerDisplay* ld = user_data;
212 
213  if (!account || !ld)
214  return FALSE;
215 
216  return (account == gnc_ledger_display_leader (ld));
217 }
218 
219 static gboolean
220 find_by_query (gpointer find_data, gpointer user_data)
221 {
222  Query* q = find_data;
223  GNCLedgerDisplay* ld = user_data;
224 
225  if (ld->reg->type != SEARCH_LEDGER)
226  return FALSE;
227 
228  if (!q || !ld)
229  return FALSE;
230 
231  return ld->query == q;
232 }
233 
234 static gboolean
235 find_by_reg (gpointer find_data, gpointer user_data)
236 {
237  SplitRegister* reg = find_data;
238  GNCLedgerDisplay* ld = user_data;
239 
240  if (!reg || !ld)
241  return FALSE;
242 
243  return ld->reg == reg;
244 }
245 
246 static SplitRegisterStyle
247 gnc_get_default_register_style (GNCAccountType type)
248 {
249  SplitRegisterStyle new_style = REG_STYLE_LEDGER;
250 
251  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
252  GNC_PREF_DEFAULT_STYLE_JOURNAL))
253  new_style = REG_STYLE_JOURNAL;
254  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
255  GNC_PREF_DEFAULT_STYLE_AUTOLEDGER))
256  new_style = REG_STYLE_AUTO_LEDGER;
257 
258  return new_style;
259 }
260 
261 static gpointer
262 look_for_portfolio_cb (Account* account, gpointer data)
263 {
264  return xaccAccountIsPriced (account) ? (gpointer) PORTFOLIO_LEDGER : NULL;
265 }
266 
267 static SplitRegisterType
268 gnc_get_reg_type (Account* leader, GNCLedgerDisplayType ld_type)
269 {
270  GNCAccountType account_type;
271  SplitRegisterType reg_type;
272 
273  if (ld_type == LD_GL)
274  return GENERAL_JOURNAL;
275 
276  account_type = xaccAccountGetType (leader);
277 
278  if (ld_type == LD_SINGLE)
279  {
280  switch (account_type)
281  {
282  case ACCT_TYPE_BANK:
283  return BANK_REGISTER;
284 
285  case ACCT_TYPE_CASH:
286  return CASH_REGISTER;
287 
288  case ACCT_TYPE_ASSET:
289  return ASSET_REGISTER;
290 
291  case ACCT_TYPE_CREDIT:
292  return CREDIT_REGISTER;
293 
294  case ACCT_TYPE_LIABILITY:
295  return LIABILITY_REGISTER;
296 
297  case ACCT_TYPE_PAYABLE:
298  return PAYABLE_REGISTER;
299 
301  return RECEIVABLE_REGISTER;
302 
303  case ACCT_TYPE_STOCK:
304  case ACCT_TYPE_MUTUAL:
305  return STOCK_REGISTER;
306 
307  case ACCT_TYPE_INCOME:
308  return INCOME_REGISTER;
309 
310  case ACCT_TYPE_EXPENSE:
311  return EXPENSE_REGISTER;
312 
313  case ACCT_TYPE_EQUITY:
314  return EQUITY_REGISTER;
315 
316  case ACCT_TYPE_CURRENCY:
317  return CURRENCY_REGISTER;
318 
319  case ACCT_TYPE_TRADING:
320  return TRADING_REGISTER;
321 
322  default:
323  PERR ("unknown account type %d\n", account_type);
324  return BANK_REGISTER;
325  }
326  }
327 
328  if (ld_type != LD_SUBACCOUNT)
329  {
330  PERR ("unknown ledger type %d\n", ld_type);
331  return BANK_REGISTER;
332  }
333 
334  switch (account_type)
335  {
336  case ACCT_TYPE_BANK:
337  case ACCT_TYPE_CASH:
338  case ACCT_TYPE_ASSET:
339  case ACCT_TYPE_CREDIT:
340  case ACCT_TYPE_LIABILITY:
342  case ACCT_TYPE_PAYABLE:
343  {
344  /* If any of the sub-accounts have ACCT_TYPE_STOCK or
345  * ACCT_TYPE_MUTUAL types, then we must use the PORTFOLIO_LEDGER
346  * ledger. Otherwise, a plain old GENERAL_JOURNAL will do. */
347  gpointer ret;
348  reg_type = GENERAL_JOURNAL;
349 
350  ret = gnc_account_foreach_descendant_until (leader, look_for_portfolio_cb,
351  NULL);
352  if (ret) reg_type = PORTFOLIO_LEDGER;
353  break;
354  }
355 
356  case ACCT_TYPE_STOCK:
357  case ACCT_TYPE_MUTUAL:
358  case ACCT_TYPE_CURRENCY:
359  reg_type = PORTFOLIO_LEDGER;
360  break;
361 
362  case ACCT_TYPE_INCOME:
363  case ACCT_TYPE_EXPENSE:
364  reg_type = INCOME_LEDGER;
365  break;
366 
367  case ACCT_TYPE_EQUITY:
368  case ACCT_TYPE_TRADING:
369  reg_type = GENERAL_JOURNAL;
370  break;
371 
372  default:
373  PERR ("unknown account type:%d", account_type);
374  reg_type = GENERAL_JOURNAL;
375  break;
376  }
377 
378  return reg_type;
379 }
380 
381 /* Returns a boolean of whether this display should be single or double lined
382  * mode by default */
383 gboolean
385 {
386  return (gld->use_double_line_default ||
387  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
388  GNC_PREF_DOUBLE_LINE_MODE));
389 }
390 
391 /* Opens up a register window to display a single account */
392 GNCLedgerDisplay*
394 {
395  SplitRegisterType reg_type;
396  GNCAccountType acc_type = xaccAccountGetType (account);
397  gboolean use_double_line;
398  GNCLedgerDisplay* ld;
399 
400  ENTER ("account=%p", account);
401 
402  switch (acc_type)
403  {
404  case ACCT_TYPE_PAYABLE:
406  use_double_line = TRUE;
407  break;
408  default:
409  use_double_line = FALSE;
410  break;
411  }
412 
413  reg_type = gnc_get_reg_type (account, LD_SINGLE);
414 
415  ld = gnc_ledger_display_internal (account, NULL, LD_SINGLE, reg_type,
416  gnc_get_default_register_style (acc_type),
417  use_double_line, FALSE, FALSE);
418  LEAVE ("%p", ld);
419  return ld;
420 }
421 
422 /* Opens up a register window to display an account, and all of its
423  * children, in the same window */
424 GNCLedgerDisplay*
426  gboolean mismatched_commodities)
427 {
428  SplitRegisterType reg_type;
429  GNCLedgerDisplay* ld;
430 
431  ENTER ("account=%p", account);
432 
433  reg_type = gnc_get_reg_type (account, LD_SUBACCOUNT);
434 
435  ld = gnc_ledger_display_internal (account, NULL, LD_SUBACCOUNT,
436  reg_type, REG_STYLE_JOURNAL, FALSE,
437  FALSE, mismatched_commodities);
438  LEAVE ("%p", ld);
439  return ld;
440 }
441 
442 /* Opens up a general journal window. */
443 GNCLedgerDisplay*
445 {
446  Query* query;
447  time64 start;
448  GNCLedgerDisplay* ld;
449  GHashTable *exclude_template_accounts_hash;
450 
451  ENTER (" ");
452 
453  query = qof_query_create_for (GNC_ID_SPLIT);
454 
455  qof_query_set_book (query, gnc_get_current_book());
456 
457  exclude_template_accounts_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
458 
459  /* In lieu of not "mis-using" some portion of the infrastructure by writing
460  * a bunch of new code, we just filter out the accounts of the template
461  * transactions. While these are in a separate Account trees just for this
462  * reason, the query engine makes no distinction between Account trees.
463  * See Gnome Bug 86302.
464  * -- jsled */
465  // Exclude any template accounts for search register and gl
466  exclude_template_accounts (query, exclude_template_accounts_hash);
467 
468  // the default is to show last 30 days
469  static const time64 secs_per_thirty_days = 2592000;
470  start = gnc_time64_get_today_start () - secs_per_thirty_days;
471 
472  xaccQueryAddDateMatchTT (query,
473  TRUE, start,
474  FALSE, 0,
475  QOF_QUERY_AND);
476 
477  ld = gnc_ledger_display_internal (NULL, query, LD_GL, GENERAL_JOURNAL,
478  REG_STYLE_JOURNAL, FALSE, FALSE, FALSE);
479 
480  ld->excluded_template_acc_hash = exclude_template_accounts_hash;
481  LEAVE ("%p", ld);
482 
483  qof_query_destroy (query);
484  return ld;
485 }
486 
495 GNCLedgerDisplay*
497 {
498  QofBook* book;
499  Query* q;
500  GNCLedgerDisplay* ld;
501  SplitRegister* sr;
502  Account* root, *acct;
503  gboolean isTemplateModeTrue;
504 
505  ENTER ("id=%s", id ? id : "(null)");
506 
507  acct = NULL;
508  isTemplateModeTrue = TRUE;
509 
510  q = qof_query_create_for (GNC_ID_SPLIT);
511 
512  book = gnc_get_current_book();
513  qof_query_set_book (q, book);
514 
515  if (id != NULL)
516  {
517  root = gnc_book_get_template_root (book);
518  acct = gnc_account_lookup_by_name (root, id);
519  g_assert (acct);
520  xaccQueryAddSingleAccountMatch (q, acct, QOF_QUERY_AND);
521  }
522 
523  ld = gnc_ledger_display_internal (NULL, q, LD_GL,
524  SEARCH_LEDGER,
525  REG_STYLE_JOURNAL,
526  FALSE,
527  isTemplateModeTrue,
528  FALSE);
529 
531  if (acct)
532  {
534  }
535 
536  qof_query_destroy (q);
537 
538  LEAVE ("%p", ld);
539  return ld;
540 }
541 
542 GtkWidget*
543 gnc_ledger_display_get_parent (GNCLedgerDisplay* ld)
544 {
545  if (ld == NULL)
546  return NULL;
547 
548  if (ld->get_parent == NULL)
549  return NULL;
550 
551  return ld->get_parent (ld);
552 }
553 
554 static GtkWidget*
555 gnc_ledger_display_parent (void* user_data)
556 {
557  GNCLedgerDisplay* ld = user_data;
558  return gnc_ledger_display_get_parent (ld);
559 }
560 
561 static void
562 gnc_ledger_display_set_watches (GNCLedgerDisplay* ld, GList* splits)
563 {
564  GList* node;
565 
566  gnc_gui_component_clear_watches (ld->component_id);
567 
568  gnc_gui_component_watch_entity_type (ld->component_id,
569  GNC_ID_ACCOUNT,
570  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY
571  | GNC_EVENT_ITEM_CHANGED);
572 
573  for (node = splits; node; node = node->next)
574  {
575  Split* split = node->data;
576  Transaction* trans = xaccSplitGetParent (split);
577 
578  gnc_gui_component_watch_entity (ld->component_id,
579  xaccTransGetGUID (trans),
580  QOF_EVENT_MODIFY);
581  }
582 }
583 
584 static void
585 refresh_handler (GHashTable* changes, gpointer user_data)
586 {
587  GNCLedgerDisplay* ld = user_data;
588  const EventInfo* info;
589  gboolean has_leader;
590 
591  ENTER ("changes=%p, user_data=%p", changes, user_data);
592 
593  if (ld->loading)
594  {
595  LEAVE ("already loading");
596  return;
597  }
598 
599  has_leader = (ld->ld_type == LD_SINGLE || ld->ld_type == LD_SUBACCOUNT);
600 
601  if (has_leader)
602  {
603  Account* leader = gnc_ledger_display_leader (ld);
604  if (!leader)
605  {
606  gnc_close_gui_component (ld->component_id);
607  LEAVE ("no leader");
608  return;
609  }
610  }
611 
612  if (changes && has_leader)
613  {
614  info = gnc_gui_get_entity_events (changes, &ld->leader);
615  if (info && (info->event_mask & QOF_EVENT_DESTROY))
616  {
617  gnc_close_gui_component (ld->component_id);
618  LEAVE ("destroy");
619  return;
620  }
621  }
622 
623  if (ld->visible)
624  {
625  DEBUG ("immediate refresh because ledger is visible");
627  }
628  else
629  {
630  ld->needs_refresh = TRUE;
631  }
632  LEAVE (" ");
633 }
634 
635 static void
636 close_handler (gpointer user_data)
637 {
638  GNCLedgerDisplay* ld = user_data;
639 
640  if (!ld)
641  return;
642 
643  gnc_unregister_gui_component (ld->component_id);
644  ld->component_id = NO_COMPONENT;
645 
646  if (ld->destroy)
647  ld->destroy (ld);
648 
649  gnc_split_register_destroy (ld->reg);
650  ld->reg = NULL;
651 
652  // Destroy the excluded template account hash
653  if (ld->excluded_template_acc_hash)
654  g_hash_table_destroy (ld->excluded_template_acc_hash);
655 
656  qof_query_destroy (ld->query);
657  ld->query = NULL;
658 
659  qof_query_destroy (ld->pre_filter_query);
660  ld->pre_filter_query = NULL;
661 
662  g_free (ld);
663 }
664 
665 static void
666 gnc_ledger_display_make_query (GNCLedgerDisplay* ld,
667  gint limit,
668  SplitRegisterType type)
669 {
670  Account* leader;
671  GList* accounts;
672 
673  if (!ld)
674  return;
675 
676  switch (ld->ld_type)
677  {
678  case LD_SINGLE:
679  case LD_SUBACCOUNT:
680  break;
681 
682  case LD_GL:
683  return;
684 
685  default:
686  PERR ("unknown ledger type: %d", ld->ld_type);
687  return;
688  }
689 
690  qof_query_destroy (ld->query);
691  ld->query = qof_query_create_for (GNC_ID_SPLIT);
692 
693  /* This is a bit of a hack. The number of splits should be
694  * configurable, or maybe we should go back a time range instead
695  * of picking a number, or maybe we should be able to exclude
696  * based on reconciled status. Anyway, this works for now. */
697  if ((limit != 0) && (type != SEARCH_LEDGER))
698  qof_query_set_max_results (ld->query, limit);
699 
700  qof_query_set_book (ld->query, gnc_get_current_book());
701 
702  leader = gnc_ledger_display_leader (ld);
703 
704  /* if this is a subaccount ledger, record the number of
705  * subaccounts so we can determine if the query needs
706  * recreating on a refresh. */
707  if (ld->ld_type == LD_SUBACCOUNT)
708  {
709  accounts = gnc_account_get_descendants (leader);
710  ld->number_of_subaccounts = g_list_length (accounts);
711  }
712  else
713  accounts = NULL;
714 
715  accounts = g_list_prepend (accounts, leader);
716 
717  xaccQueryAddAccountMatch (ld->query, accounts,
718  QOF_GUID_MATCH_ANY, QOF_QUERY_AND);
719 
720  g_list_free (accounts);
721 }
722 
723 /* Opens up a ledger window for an arbitrary query. */
724 GNCLedgerDisplay*
726  SplitRegisterStyle style)
727 {
728  GNCLedgerDisplay* ld;
729 
730  ENTER ("query=%p", query);
731 
732  ld = gnc_ledger_display_internal (NULL, query, LD_GL, type, style,
733  FALSE, FALSE, FALSE);
734 
735  ld->excluded_template_acc_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
736  LEAVE ("%p", ld);
737  return ld;
738 }
739 
740 static GNCLedgerDisplay*
741 gnc_ledger_display_internal (Account* lead_account, Query* q,
742  GNCLedgerDisplayType ld_type,
743  SplitRegisterType reg_type,
744  SplitRegisterStyle style,
745  gboolean use_double_line,
746  gboolean is_template,
747  gboolean mismatched_commodities)
748 {
749  GNCLedgerDisplay* ld;
750  gint limit;
751  const char* klass;
752 
753  switch (ld_type)
754  {
755  case LD_SINGLE:
756  klass = REGISTER_SINGLE_CM_CLASS;
757 
758  if (reg_type >= NUM_SINGLE_REGISTER_TYPES)
759  {
760  PERR ("single-account register with wrong split register type");
761  return NULL;
762  }
763 
764  if (!lead_account)
765  {
766  PERR ("single-account register with no account specified");
767  return NULL;
768  }
769 
770  if (q)
771  {
772  PWARN ("single-account register with external query");
773  q = NULL;
774  }
775 
776  ld = gnc_find_first_gui_component (klass, find_by_leader, lead_account);
777  if (ld)
778  return ld;
779 
780  break;
781 
782  case LD_SUBACCOUNT:
783  klass = REGISTER_SUBACCOUNT_CM_CLASS;
784 
785  if (!lead_account)
786  {
787  PERR ("sub-account register with no lead account");
788  return NULL;
789  }
790 
791  if (q)
792  {
793  PWARN ("account register with external query");
794  q = NULL;
795  }
796 
797  ld = gnc_find_first_gui_component (klass, find_by_leader, lead_account);
798  if (ld)
799  return ld;
800 
801  break;
802 
803  case LD_GL:
804  klass = REGISTER_GL_CM_CLASS;
805 
806  if (!q)
807  {
808  PWARN ("general journal with no query");
809  }
810 
811  break;
812 
813  default:
814  PERR ("bad ledger type: %d", ld_type);
815  return NULL;
816 
817  }
818 
819  ld = g_new (GNCLedgerDisplay, 1);
820 
821  ld->leader = *xaccAccountGetGUID (lead_account);
822  ld->query = NULL;
823  ld->ld_type = ld_type;
824  ld->loading = FALSE;
825  ld->visible = FALSE;
826  ld->needs_refresh = TRUE;
827  ld->destroy = NULL;
828  ld->get_parent = NULL;
829  ld->user_data = NULL;
830  ld->excluded_template_acc_hash = NULL;
831 
832  limit = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REGISTER,
833  GNC_PREF_MAX_TRANS);
834 
835  /* set up the query filter */
836  if (q)
837  ld->query = qof_query_copy (q);
838  else
839  gnc_ledger_display_make_query (ld, limit, reg_type);
840 
841  ld->pre_filter_query = qof_query_copy (ld->query);
842 
843  ld->component_id = gnc_register_gui_component (klass,
844  refresh_handler,
845  close_handler, ld);
846 
847  /******************************************************************\
848  * The main register window itself *
849  \******************************************************************/
850 
851  ld->use_double_line_default = use_double_line;
852 
853  ld->reg = gnc_split_register_new (reg_type, style, use_double_line,
854  is_template, mismatched_commodities);
855 
856  gnc_split_register_set_data (ld->reg, ld, gnc_ledger_display_parent);
857 
858  /* Must call this before gnc_table_realize_gui() gets called or all the
859  * combo boxes will be empty. Use an empty list of splits instad of running
860  * the query when we're not in focus yet.
861  */
862  ld->loading = TRUE;
863  gnc_split_register_load (ld->reg, NULL, NULL, gnc_ledger_display_leader (ld));
864  ld->loading = FALSE;
865  return ld;
866 }
867 
868 void
869 gnc_ledger_display_set_query (GNCLedgerDisplay* ledger_display, Query* q)
870 {
871  if (!ledger_display || !q)
872  return;
873 
874  g_return_if_fail (ledger_display->ld_type == LD_GL);
875 
876  qof_query_destroy (ledger_display->query);
877  ledger_display->query = qof_query_copy (q);
878 }
879 
880 GNCLedgerDisplay*
882 {
883  if (!q)
884  return NULL;
885 
886  return gnc_find_first_gui_component (REGISTER_GL_CM_CLASS, find_by_query, q);
887 }
888 
889 /********************************************************************\
890  * refresh only the indicated register window *
891 \********************************************************************/
892 
893 static void
894 gnc_ledger_display_refresh_internal (GNCLedgerDisplay* ld)
895 {
896  GList* splits;
897  GList* pre_filter_splits = NULL;
898 
899  if (ld->loading)
900  return;
901 
902  /* It's not clear if we should re-run the query, or if we should
903  * just use qof_query_last_run(). It's possible that the dates
904  * changed, requiring a full new query. Similar considerations
905  * needed for multi-user mode.
906  */
907  splits = qof_query_run (ld->query);
908 
909  if (!qof_query_equal (ld->query, ld->pre_filter_query))
910  pre_filter_splits = qof_query_run (ld->pre_filter_query);
911 
912  gnc_ledger_display_set_watches (ld, splits);
913 
914  if (!gnc_split_register_full_refresh_ok (ld->reg))
915  return;
916 
917  ld->loading = TRUE;
918 
919  gnc_split_register_load (ld->reg, splits, pre_filter_splits,
921 
922  ld->needs_refresh = FALSE;
923  ld->loading = FALSE;
924 }
925 
926 void
927 gnc_ledger_display_refresh (GNCLedgerDisplay* ld)
928 {
929  ENTER ("ld=%p", ld);
930 
931  if (!ld)
932  {
933  LEAVE ("no display");
934  return;
935  }
936 
937  if (ld->loading)
938  {
939  LEAVE ("already loading");
940  return;
941  }
942 
943  /* if subaccount ledger, check to see if still the same number
944  * of subaccounts, if not recreate the query. */
945  if (ld->ld_type == LD_SUBACCOUNT)
946  {
947  Account* leader = gnc_ledger_display_leader (ld);
948  GList* accounts = gnc_account_get_descendants (leader);
949 
950  if (g_list_length (accounts) != ld->number_of_subaccounts)
951  {
952  time64 start_time, end_time;
953  xaccQueryGetDateMatchTT (ld->query, &start_time, &end_time);
954 
955  cleared_match_t cleared_match = xaccQueryGetClearedMatch (ld->query);
956 
957  gnc_ledger_display_make_query (ld,
958  gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REGISTER, GNC_PREF_MAX_TRANS),
959  gnc_get_reg_type (leader, ld->ld_type));
960 
961  qof_query_destroy (ld->pre_filter_query);
962  ld->pre_filter_query = qof_query_copy (ld->query);
963 
964  if (cleared_match != CLEARED_ALL)
965  xaccQueryAddClearedMatch (ld->query, cleared_match, QOF_QUERY_AND);
966 
967  if (start_time || end_time)
968  {
969  xaccQueryAddDateMatchTT (ld->query,
970  start_time != 0, start_time,
971  end_time != 0, end_time,
972  QOF_QUERY_AND);
973  }
974  }
975  g_list_free (accounts);
976  }
977 
978  /* In lieu of not "mis-using" some portion of the infrastructure by writing
979  * a bunch of new code, we just filter out the accounts of the template
980  * transactions. While these are in a separate Account trees just for this
981  * reason, the query engine makes no distinction between Account trees.
982  * See Gnome Bug 86302.
983  * -- jsled */
984  // Exclude any template accounts for search register and gl
985  if (!ld->reg->is_template && (ld->reg->type == SEARCH_LEDGER || ld->ld_type == LD_GL))
986  {
987  exclude_template_accounts (ld->query, ld->excluded_template_acc_hash);
988 
989  qof_query_destroy (ld->pre_filter_query);
990  ld->pre_filter_query = qof_query_copy (ld->query);
991  }
992  gnc_ledger_display_refresh_internal (ld);
993  LEAVE (" ");
994 }
995 
996 void gnc_ledger_display_set_focus (GNCLedgerDisplay* ld, gboolean focus)
997 {
998  if (!ld)
999  return;
1000 
1001  ld->visible = focus;
1002 
1003  if (ld->visible && ld->needs_refresh)
1004  {
1005  DEBUG ("deferred refresh because ledger is now visible");
1007  }
1008 }
1009 
1010 void
1011 gnc_ledger_display_refresh_by_split_register (SplitRegister* reg)
1012 {
1013  GNCLedgerDisplay* ld;
1014 
1015  if (!reg)
1016  return;
1017 
1018  ld = gnc_find_first_gui_component (REGISTER_SINGLE_CM_CLASS,
1019  find_by_reg, reg);
1020  if (ld)
1021  {
1023  return;
1024  }
1025 
1026  ld = gnc_find_first_gui_component (REGISTER_SUBACCOUNT_CM_CLASS,
1027  find_by_reg, reg);
1028  if (ld)
1029  {
1031  return;
1032  }
1033 
1034  ld = gnc_find_first_gui_component (REGISTER_GL_CM_CLASS,
1035  find_by_reg, reg);
1036  if (ld)
1037  {
1039  return;
1040  }
1041 
1042  ld = gnc_find_first_gui_component (REGISTER_TEMPLATE_CM_CLASS,
1043  find_by_reg, reg);
1044  if (ld)
1045  {
1047  }
1048 }
1049 
1050 void
1051 gnc_ledger_display_close (GNCLedgerDisplay* ld)
1052 {
1053  if (!ld)
1054  return;
1055 
1056  gnc_close_gui_component (ld->component_id);
1057 }
void gnc_ledger_display_close(GNCLedgerDisplay *ld)
close the window
Public declarations for GncLedgerDisplay class.
void gnc_ledger_display_set_focus(GNCLedgerDisplay *ld, gboolean focus)
Mark the ledger as being in focus (refresh immediately) or not.
void gnc_ledger_display_refresh(GNCLedgerDisplay *ld)
redisplay/redraw only the indicated window.
gboolean gnc_ledger_display_default_double_line(GNCLedgerDisplay *gld)
Returns a boolean of whether this display should be single or double lined mode by default...
Date and Time handling routines.
void gnc_split_register_destroy(SplitRegister *reg)
Destroys a split register.
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false. ...
Definition: Account.cpp:4640
utility functions for the GnuCash UI
Expense accounts are used to denote expenses.
Definition: Account.h:143
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3228
STRUCTS.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
Mutual Fund accounts will typically be shown in registers which show three columns: price...
Definition: Account.h:125
gpointer gnc_account_foreach_descendant_until(const Account *acc, AccountCb2 thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3205
TableControl specialized for the SplitRegister.
gboolean gnc_split_register_full_refresh_ok(SplitRegister *reg)
Private function – outsiders must not use this.
QofQuery * qof_query_copy(QofQuery *q)
Make a copy of the indicated query.
Definition: qofquery.cpp:1018
gboolean qof_query_equal(const QofQuery *q1, const QofQuery *q2)
Compare two queries for equality.
Definition: qofquery.cpp:1472
Account * gnc_ledger_display_leader(GNCLedgerDisplay *ld)
Implementations.
void gnc_split_register_set_template_account(SplitRegister *reg, Account *template_account)
Set the template account for use in a template register.
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
Definition: SX-book.cpp:65
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
TableModels specialized for SplitRegister and template SplitRegister.
void gnc_ledger_display_set_query(GNCLedgerDisplay *ledger_display, Query *q)
Set the query used for a register.
void gnc_split_register_set_data(SplitRegister *reg, gpointer user_data, SRGetParentCallback get_parent)
Sets the user data and callback hooks for the register.
void gnc_split_register_load(SplitRegister *reg, GList *slist, GList *pre_filter_slist, Account *default_account)
Populates the rows of a register.
These expect a single object and expect the QofAccessFunc returns GncGUID*.
Definition: qofquerycore.h:113
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
The cash account type is used to denote a shoe-box or pillowcase stuffed with * cash.
Definition: Account.h:110
Query * gnc_ledger_display_get_query(GNCLedgerDisplay *ld)
return the query associated with a ledger
Account used to record multiple commodity transactions.
Definition: Account.h:155
void qof_query_set_max_results(QofQuery *q, int n)
Set the maximum number of results that should be returned.
Definition: qofquery.cpp:1289
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
The gnc_account_lookup_by_name() subroutine fetches the account by name from the descendants of the s...
Definition: Account.cpp:3054
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
Stock accounts will typically be shown in registers which show three columns: price, number of shares, and value.
Definition: Account.h:122
GNCLedgerDisplay * gnc_ledger_display_subaccounts(Account *account, gboolean mismatched_commodities)
opens up a register window to display the parent account and all of its children. ...
#define xaccAccountGetGUID(X)
Definition: Account.h:248
Account handling public routines.
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
void gnc_ledger_display_set_handlers(GNCLedgerDisplay *ld, GNCLedgerDisplayDestroy destroy, GNCLedgerDisplayGetParent get_parent)
set the handlers used by the ledger display
Income accounts are used to denote income.
Definition: Account.h:140
The GNCLedgerDisplay struct describes a single register/ledger instance.
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
Anchor Scheduled Transaction info in a book.
The bank account type denotes a savings or checking account held at a bank.
Definition: Account.h:107
time64 gnc_time64_get_today_start(void)
The gnc_time64_get_today_start() routine returns a time64 value corresponding to the first second of ...
Definition: gnc-date.cpp:1346
A/P account type.
Definition: Account.h:151
Additional event handling code.
GtkWidget * gnc_ledger_display_get_parent(GNCLedgerDisplay *ld)
Returns the parent of a given ledger display.
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:116
All type declarations for the whole Gnucash engine.
The currency account type indicates that the account is a currency trading account.
Definition: Account.h:129
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
GLib helper routines.
#define xaccTransGetGUID(X)
Definition: Transaction.h:788
Generic api to store and retrieve preferences.
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition: Account.cpp:3005
GNCLedgerDisplay * gnc_ledger_display_template_gl(char *id)
Displays a template ledger.
GList * qof_query_run(QofQuery *query)
Perform the query, return the results.
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:119
GNCLedgerDisplay * gnc_ledger_display_simple(Account *account)
opens up a register window to display a single account
GNCLedgerDisplay * gnc_ledger_display_find_by_query(Query *q)
If the given ledger display still exists, return it.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
A/R account type.
Definition: Account.h:149
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
GNCLedgerDisplay * gnc_ledger_display_query(Query *query, SplitRegisterType type, SplitRegisterStyle style)
display a general ledger for an arbitrary query
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
GNCLedgerDisplay * gnc_ledger_display_gl(void)
opens up a general ledger window
SplitRegister * gnc_ledger_display_get_split_register(GNCLedgerDisplay *ld)
return the split register associated with a ledger display
Equity account is used to balance the balance sheet.
Definition: Account.h:146
SplitRegisterType
Register types.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
API for Transactions and Splits (journal entries)
The type used to store guids in C.
Definition: guid.h:75
A Query.
Definition: qofquery.cpp:74
void gnc_ledger_display_set_user_data(GNCLedgerDisplay *ld, gpointer user_data)
get and set the user data associated with the ledger
SplitRegister * gnc_split_register_new(SplitRegisterType type, SplitRegisterStyle style, gboolean use_double_line, gboolean is_template, gboolean mismatched_commodities)
Creates a new split register.
SplitRegisterStyle
Register styles.
The Credit card account is used to denote credit (e.g.
Definition: Account.h:113
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2048