1
/********************************************************************\
2
* gnc-ledger-display.c -- utilities for dealing with multiple *
3
* register/ledger windows in GnuCash *
5
* Copyright (C) 1997 Robin D. Clark *
6
* Copyright (C) 1997, 1998 Linas Vepstas *
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. *
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. *
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. *
22
\********************************************************************/
30
#include "QueryCore.h"
33
#include "Transaction.h"
34
#include "gnc-component-manager.h"
37
#include "gnc-engine.h"
38
#include "gnc-event.h"
39
#include "gnc-gconf-utils.h"
40
#include "gnc-ledger-display.h"
41
#include "gnc-ui-util.h"
42
#include "split-register-control.h"
43
#include "split-register-model.h"
46
#define REGISTER_SINGLE_CM_CLASS "register-single"
47
#define REGISTER_SUBACCOUNT_CM_CLASS "register-subaccount"
48
#define REGISTER_GL_CM_CLASS "register-gl"
49
#define REGISTER_TEMPLATE_CM_CLASS "register-template"
52
struct gnc_ledger_display
58
GNCLedgerDisplayType ld_type;
63
gboolean use_double_line_default;
65
GNCLedgerDisplayDestroy destroy;
66
GNCLedgerDisplayGetParent get_parent;
74
/** GLOBALS *********************************************************/
75
static QofLogModule log_module = GNC_MOD_LEDGER;
78
/** Declarations ****************************************************/
79
static GNCLedgerDisplay *
80
gnc_ledger_display_internal (Account *lead_account, Query *q,
81
GNCLedgerDisplayType ld_type,
82
SplitRegisterType reg_type,
83
SplitRegisterStyle style,
84
gboolean use_double_line,
85
gboolean is_template);
86
static void gnc_ledger_display_refresh_internal (GNCLedgerDisplay *ld,
90
/** Implementations *************************************************/
93
gnc_ledger_display_leader (GNCLedgerDisplay *ld)
98
return xaccAccountLookup (&ld->leader, gnc_get_current_book ());
102
gnc_ledger_display_type (GNCLedgerDisplay *ld)
111
gnc_ledger_display_set_user_data (GNCLedgerDisplay *ld, gpointer user_data)
116
ld->user_data = user_data;
120
gnc_ledger_display_get_user_data (GNCLedgerDisplay *ld)
125
return ld->user_data;
129
gnc_ledger_display_set_handlers (GNCLedgerDisplay *ld,
130
GNCLedgerDisplayDestroy destroy,
131
GNCLedgerDisplayGetParent get_parent)
136
ld->destroy = destroy;
137
ld->get_parent = get_parent;
141
gnc_ledger_display_get_split_register (GNCLedgerDisplay *ld)
150
gnc_ledger_display_get_query (GNCLedgerDisplay *ld)
159
find_by_leader (gpointer find_data, gpointer user_data)
161
Account *account = find_data;
162
GNCLedgerDisplay *ld = user_data;
167
return (account == gnc_ledger_display_leader (ld));
171
find_by_query (gpointer find_data, gpointer user_data)
173
Query *q = find_data;
174
GNCLedgerDisplay *ld = user_data;
179
return ld->query == q;
183
find_by_reg (gpointer find_data, gpointer user_data)
185
SplitRegister *reg = find_data;
186
GNCLedgerDisplay *ld = user_data;
191
return ld->reg == reg;
194
static SplitRegisterStyle
195
gnc_get_default_register_style (GNCAccountType type)
197
SplitRegisterStyle new_style = REG_STYLE_LEDGER;
202
case ACCT_TYPE_PAYABLE:
203
case ACCT_TYPE_RECEIVABLE:
204
new_style = REG_STYLE_LEDGER;
209
style_string = gnc_gconf_get_string(GCONF_GENERAL_REGISTER,
210
"default_style", NULL);
211
if (safe_strcmp(style_string, "journal") == 0)
212
new_style = REG_STYLE_JOURNAL;
213
else if (safe_strcmp(style_string, "auto_ledger") == 0)
214
new_style = REG_STYLE_AUTO_LEDGER;
216
new_style = REG_STYLE_LEDGER;
218
if (style_string != NULL)
219
g_free(style_string);
228
look_for_portfolio_cb (Account *account, gpointer data)
230
return xaccAccountIsPriced(account) ? (gpointer) PORTFOLIO_LEDGER : NULL;
233
static SplitRegisterType
234
gnc_get_reg_type (Account *leader, GNCLedgerDisplayType ld_type)
236
GNCAccountType account_type;
237
SplitRegisterType reg_type;
239
if (ld_type == LD_GL)
240
return GENERAL_LEDGER;
242
account_type = xaccAccountGetType (leader);
244
if (ld_type == LD_SINGLE)
246
switch (account_type)
249
return BANK_REGISTER;
252
return CASH_REGISTER;
254
case ACCT_TYPE_ASSET:
255
return ASSET_REGISTER;
257
case ACCT_TYPE_CREDIT:
258
return CREDIT_REGISTER;
260
case ACCT_TYPE_LIABILITY:
261
return LIABILITY_REGISTER;
263
case ACCT_TYPE_PAYABLE:
264
return PAYABLE_REGISTER;
266
case ACCT_TYPE_RECEIVABLE:
267
return RECEIVABLE_REGISTER;
269
case ACCT_TYPE_STOCK:
270
case ACCT_TYPE_MUTUAL:
271
return STOCK_REGISTER;
273
case ACCT_TYPE_INCOME:
274
return INCOME_REGISTER;
276
case ACCT_TYPE_EXPENSE:
277
return EXPENSE_REGISTER;
279
case ACCT_TYPE_EQUITY:
280
return EQUITY_REGISTER;
282
case ACCT_TYPE_CURRENCY:
283
return CURRENCY_REGISTER;
286
PERR ("unknown account type %d\n", account_type);
287
return BANK_REGISTER;
291
if (ld_type != LD_SUBACCOUNT)
293
PERR ("unknown ledger type %d\n", ld_type);
294
return BANK_REGISTER;
297
switch (account_type)
301
case ACCT_TYPE_ASSET:
302
case ACCT_TYPE_CREDIT:
303
case ACCT_TYPE_LIABILITY:
304
case ACCT_TYPE_RECEIVABLE:
305
case ACCT_TYPE_PAYABLE:
307
/* If any of the sub-accounts have ACCT_TYPE_STOCK or
308
* ACCT_TYPE_MUTUAL types, then we must use the PORTFOLIO_LEDGER
309
* ledger. Otherwise, a plain old GENERAL_LEDGER will do. */
311
reg_type = GENERAL_LEDGER;
313
ret = gnc_account_foreach_descendant_until(leader, look_for_portfolio_cb, NULL);
314
if (ret) reg_type = PORTFOLIO_LEDGER;
318
case ACCT_TYPE_STOCK:
319
case ACCT_TYPE_MUTUAL:
320
case ACCT_TYPE_CURRENCY:
321
reg_type = PORTFOLIO_LEDGER;
324
case ACCT_TYPE_INCOME:
325
case ACCT_TYPE_EXPENSE:
326
reg_type = INCOME_LEDGER;
329
case ACCT_TYPE_EQUITY:
330
reg_type = GENERAL_LEDGER;
334
PERR ("unknown account type:%d", account_type);
335
reg_type = GENERAL_LEDGER;
342
/* Returns a boolean of whether this display should be single or double lined
345
gnc_ledger_display_default_double_line (GNCLedgerDisplay *gld)
347
return (gld->use_double_line_default ||
348
gnc_gconf_get_bool(GCONF_GENERAL_REGISTER, "double_line_mode", NULL));
351
/* Opens up a register window to display a single account */
353
gnc_ledger_display_simple (Account *account)
355
SplitRegisterType reg_type;
356
GNCAccountType acc_type = xaccAccountGetType (account);
357
gboolean use_double_line;
360
case ACCT_TYPE_PAYABLE:
361
case ACCT_TYPE_RECEIVABLE:
362
use_double_line = TRUE;
365
use_double_line = FALSE;
369
reg_type = gnc_get_reg_type (account, LD_SINGLE);
371
return gnc_ledger_display_internal (account, NULL, LD_SINGLE, reg_type,
372
gnc_get_default_register_style(acc_type),
373
use_double_line, FALSE);
376
/* Opens up a register window to display an account, and all of its
377
* children, in the same window */
379
gnc_ledger_display_subaccounts (Account *account)
381
SplitRegisterType reg_type;
383
reg_type = gnc_get_reg_type (account, LD_SUBACCOUNT);
385
return gnc_ledger_display_internal (account, NULL, LD_SUBACCOUNT,
386
reg_type, REG_STYLE_JOURNAL, FALSE,
390
/* Opens up a general ledger window. */
392
gnc_ledger_display_gl (void)
398
query = xaccMallocQuery ();
400
xaccQuerySetBook (query, gnc_get_current_book());
402
/* In lieu of not "mis-using" some portion of the infrastructure by writing
403
* a bunch of new code, we just filter out the accounts of the template
404
* transactions. While these are in a seperate Account trees just for this
405
* reason, the query engine makes no distinction between Account trees.
406
* See Gnome Bug 86302.
412
tRoot = gnc_book_get_template_root( gnc_get_current_book() );
413
al = gnc_account_get_descendants( tRoot );
414
xaccQueryAddAccountMatch( query, al, GUID_MATCH_NONE, QUERY_AND );
420
gnc_tm_get_today_start(&tm);
421
tm.tm_mon--; /* Default the register to the last month's worth of transactions. */
422
start = mktime (&tm);
423
xaccQueryAddDateMatchTT (query,
428
return gnc_ledger_display_internal (NULL, query, LD_GL,
430
REG_STYLE_JOURNAL, FALSE, FALSE);
434
* @param id: The string version of the GUID of the context of template
435
* transaction being edited in this template GL. As used by scheduled
436
* transactions, this is the GUID of the SX itself which is magically the
437
* *name* of the (template) account which contains the transactions for this
438
* scheduled transaction. That's right. The stringified GUID of the SX is
439
* the name of the SX'es template account.
442
gnc_ledger_display_template_gl (char *id)
446
GNCLedgerDisplay *ld;
448
Account *root, *acct;
449
gboolean isTemplateModeTrue;
452
isTemplateModeTrue = TRUE;
454
q = xaccMallocQuery ();
456
book = gnc_get_current_book ();
457
xaccQuerySetBook (q, book);
460
root = gnc_book_get_template_root (book);
461
acct = gnc_account_lookup_by_name(root, id);
463
xaccQueryAddSingleAccountMatch (q, acct, QUERY_AND);
466
ld = gnc_ledger_display_internal (NULL, q, LD_GL,
472
sr = gnc_ledger_display_get_split_register (ld);
474
gnc_split_register_set_template_account (sr, acct);
481
gnc_ledger_display_get_parent( GNCLedgerDisplay *ld )
486
if ( ld->get_parent == NULL )
489
return ld->get_parent( ld );
493
gnc_ledger_display_parent (void *user_data)
495
GNCLedgerDisplay *ld = user_data;
496
return gnc_ledger_display_get_parent( ld );
500
gnc_ledger_display_set_watches (GNCLedgerDisplay *ld, GList *splits)
504
gnc_gui_component_clear_watches (ld->component_id);
506
gnc_gui_component_watch_entity_type (ld->component_id,
508
QOF_EVENT_MODIFY | QOF_EVENT_DESTROY
509
| GNC_EVENT_ITEM_CHANGED);
511
for (node = splits; node; node = node->next)
513
Split *split = node->data;
514
Transaction *trans = xaccSplitGetParent (split);
516
gnc_gui_component_watch_entity (ld->component_id,
517
xaccTransGetGUID (trans),
523
refresh_handler (GHashTable *changes, gpointer user_data)
525
GNCLedgerDisplay *ld = user_data;
526
const EventInfo *info;
533
has_leader = (ld->ld_type == LD_SINGLE || ld->ld_type == LD_SUBACCOUNT);
537
Account *leader = gnc_ledger_display_leader (ld);
540
gnc_close_gui_component (ld->component_id);
545
if (changes && has_leader)
547
info = gnc_gui_get_entity_events (changes, &ld->leader);
548
if (info && (info->event_mask & QOF_EVENT_DESTROY))
550
gnc_close_gui_component (ld->component_id);
555
/* Its not clear if we should re-run the query, or if we should
556
* just use qof_query_last_run(). Its possible that the dates
557
* changed, requiring a full new query. Similar considerations
558
* needed for multi-user mode.
560
splits = qof_query_run (ld->query);
562
gnc_ledger_display_set_watches (ld, splits);
564
gnc_ledger_display_refresh_internal (ld, splits);
568
close_handler (gpointer user_data)
570
GNCLedgerDisplay *ld = user_data;
575
gnc_unregister_gui_component (ld->component_id);
580
gnc_split_register_destroy (ld->reg);
583
xaccFreeQuery (ld->query);
590
gnc_ledger_display_make_query (GNCLedgerDisplay *ld,
592
SplitRegisterType type)
610
PERR ("unknown ledger type: %d", ld->ld_type);
614
xaccFreeQuery (ld->query);
615
ld->query = xaccMallocQuery ();
617
/* This is a bit of a hack. The number of splits should be
618
* configurable, or maybe we should go back a time range instead
619
* of picking a number, or maybe we should be able to exclude
620
* based on reconciled status. Anyway, this works for now. */
621
if ((limit != 0) && (type != SEARCH_LEDGER))
622
xaccQuerySetMaxSplits (ld->query, limit);
624
xaccQuerySetBook (ld->query, gnc_get_current_book());
626
leader = gnc_ledger_display_leader (ld);
628
if (ld->ld_type == LD_SUBACCOUNT)
629
accounts = gnc_account_get_descendants (leader);
633
accounts = g_list_prepend (accounts, leader);
635
xaccQueryAddAccountMatch (ld->query, accounts,
636
GUID_MATCH_ANY, QUERY_AND);
638
g_list_free (accounts);
641
/* Opens up a ledger window for an arbitrary query. */
643
gnc_ledger_display_query (Query *query, SplitRegisterType type,
644
SplitRegisterStyle style)
646
return gnc_ledger_display_internal (NULL, query, LD_GL, type, style,
650
static GNCLedgerDisplay *
651
gnc_ledger_display_internal (Account *lead_account, Query *q,
652
GNCLedgerDisplayType ld_type,
653
SplitRegisterType reg_type,
654
SplitRegisterStyle style,
655
gboolean use_double_line,
656
gboolean is_template )
658
GNCLedgerDisplay *ld;
666
class = REGISTER_SINGLE_CM_CLASS;
668
if (reg_type >= NUM_SINGLE_REGISTER_TYPES)
670
PERR ("single-account register with wrong split register type");
676
PERR ("single-account register with no account specified");
682
PWARN ("single-account register with external query");
686
ld = gnc_find_first_gui_component (class, find_by_leader, lead_account);
693
class = REGISTER_SUBACCOUNT_CM_CLASS;
697
PERR ("sub-account register with no lead account");
703
PWARN ("account register with external query");
707
ld = gnc_find_first_gui_component (class, find_by_leader, lead_account);
714
class = REGISTER_GL_CM_CLASS;
718
PWARN ("general ledger with no query");
724
PERR ("bad ledger type: %d", ld_type);
729
ld = g_new (GNCLedgerDisplay, 1);
731
ld->leader = *xaccAccountGetGUID (lead_account);
733
ld->ld_type = ld_type;
736
ld->get_parent = NULL;
737
ld->user_data = NULL;
739
limit = gnc_gconf_get_float(GCONF_GENERAL_REGISTER, "max_transactions", NULL);
741
/* set up the query filter */
743
ld->query = xaccQueryCopy (q);
745
gnc_ledger_display_make_query (ld, limit, reg_type);
747
ld->component_id = gnc_register_gui_component (class,
751
/******************************************************************\
752
* The main register window itself *
753
\******************************************************************/
755
ld->use_double_line_default = use_double_line;
756
ld->reg = gnc_split_register_new (reg_type, style, use_double_line,
759
gnc_split_register_set_data (ld->reg, ld, gnc_ledger_display_parent);
761
splits = qof_query_run (ld->query);
763
gnc_ledger_display_set_watches (ld, splits);
765
gnc_ledger_display_refresh_internal (ld, splits);
771
gnc_ledger_display_set_query (GNCLedgerDisplay *ledger_display, Query *q)
773
if (!ledger_display || !q)
776
g_return_if_fail (ledger_display->ld_type == LD_GL);
778
xaccFreeQuery (ledger_display->query);
779
ledger_display->query = xaccQueryCopy (q);
783
gnc_ledger_display_find_by_query (Query *q)
788
return gnc_find_first_gui_component (REGISTER_GL_CM_CLASS, find_by_query, q);
791
/********************************************************************\
792
* refresh only the indicated register window *
793
\********************************************************************/
796
gnc_ledger_display_refresh_internal (GNCLedgerDisplay *ld, GList *splits)
798
if (!ld || ld->loading)
801
if (!gnc_split_register_full_refresh_ok (ld->reg))
806
gnc_split_register_load (ld->reg, splits,
807
gnc_ledger_display_leader (ld));
813
gnc_ledger_display_refresh (GNCLedgerDisplay *ld)
815
if (!ld || ld->loading)
818
gnc_ledger_display_refresh_internal (ld, xaccQueryGetSplits (ld->query));
822
gnc_ledger_display_refresh_by_split_register (SplitRegister *reg)
824
GNCLedgerDisplay *ld;
829
ld = gnc_find_first_gui_component (REGISTER_SINGLE_CM_CLASS,
833
gnc_ledger_display_refresh (ld);
837
ld = gnc_find_first_gui_component (REGISTER_SUBACCOUNT_CM_CLASS,
841
gnc_ledger_display_refresh (ld);
845
ld = gnc_find_first_gui_component (REGISTER_GL_CM_CLASS,
849
gnc_ledger_display_refresh (ld);
853
ld = gnc_find_first_gui_component (REGISTER_TEMPLATE_CM_CLASS,
857
gnc_ledger_display_refresh (ld);
862
gnc_ledger_display_close (GNCLedgerDisplay *ld)
867
gnc_close_gui_component (ld->component_id);