From e1b02b2280b94299510635ac91df26bfd6fb6161 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Tue, 25 Nov 1997 12:04:30 +0000 Subject: [PATCH] major revision to add general ledger capabilities git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@145 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/AccWindow.c | 22 +- src/Account.c | 183 ++-- src/AccountMenu.c | 3 + src/Action.c | 1 + src/BuildMenu.c | 3 + src/Destroy.c | 56 ++ src/LedgerUtils.c | 323 +++++++ src/MainWindow.c | 89 +- src/Makefile | 4 +- src/PopBox.c | 34 +- src/QIFIO.c | 2 +- src/RegWindow.c | 2163 ++++++++++++++++++++++++++++++++++----------- src/Transaction.c | 48 + src/XferWindow.c | 4 +- xacc.lsm | 27 +- 15 files changed, 2304 insertions(+), 658 deletions(-) create mode 100644 src/Destroy.c create mode 100644 src/LedgerUtils.c diff --git a/src/AccWindow.c b/src/AccWindow.c index 0437af25a3..87752cb399 100644 --- a/src/AccWindow.c +++ b/src/AccWindow.c @@ -641,7 +641,7 @@ createCB( Widget mw, XtPointer cd, XtPointer cb ) /* The account has to have a name! */ if( strcmp( name, "" ) == 0 ) { - errorBox (toplevel, "The account must be given a name! \n"); + errorBox (toplevel, ACC_NO_NAME_MSG); return; } @@ -683,7 +683,7 @@ createCB( Widget mw, XtPointer cd, XtPointer cb ) refreshMainWindow(); /* open up the account window for the user */ - regWindow( toplevel, acc ); + regWindowSimple ( toplevel, acc ); /* if we got to here, tear down the dialog window */ XtDestroyWidget (accData->dialog); @@ -754,12 +754,12 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) case BANK: case CASH: case ASSET: - case PORTFOLIO: + case STOCK: case MUTUAL: XtSetSensitive (menu->type_widgets[BANK], True); XtSetSensitive (menu->type_widgets[CASH], True); XtSetSensitive (menu->type_widgets[ASSET], True); - XtSetSensitive (menu->type_widgets[PORTFOLIO], True); + XtSetSensitive (menu->type_widgets[STOCK], True); XtSetSensitive (menu->type_widgets[MUTUAL], True); XtSetSensitive (menu->type_widgets[LIABILITY], False); XtSetSensitive (menu->type_widgets[CREDIT], False); @@ -776,7 +776,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) /* set a default, if an inapporpriate button is pushed */ if ((BANK != but) && (CASH != but) && - (ASSET != but) && (PORTFOLIO != but) && + (ASSET != but) && (STOCK != but) && (MUTUAL != but) ) { XtVaSetValues (menu->type_widgets[acc->type], XmNset, True, NULL); } @@ -787,7 +787,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) XtSetSensitive (menu->type_widgets[BANK], False); XtSetSensitive (menu->type_widgets[CASH], False); XtSetSensitive (menu->type_widgets[ASSET], False); - XtSetSensitive (menu->type_widgets[PORTFOLIO], False); + XtSetSensitive (menu->type_widgets[STOCK], False); XtSetSensitive (menu->type_widgets[MUTUAL], False); XtSetSensitive (menu->type_widgets[LIABILITY], True); XtSetSensitive (menu->type_widgets[CREDIT], True); @@ -799,7 +799,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) XtVaSetValues (menu->type_widgets[BANK], XmNset, False, NULL); XtVaSetValues (menu->type_widgets[CASH], XmNset, False, NULL); XtVaSetValues (menu->type_widgets[ASSET], XmNset, False, NULL); - XtVaSetValues (menu->type_widgets[PORTFOLIO], XmNset, False, NULL); + XtVaSetValues (menu->type_widgets[STOCK], XmNset, False, NULL); XtVaSetValues (menu->type_widgets[MUTUAL], XmNset, False, NULL); XtVaSetValues (menu->type_widgets[INCOME], XmNset, False, NULL); XtVaSetValues (menu->type_widgets[EXPENSE], XmNset, False, NULL); @@ -815,7 +815,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) XtSetSensitive (menu->type_widgets[BANK], False); XtSetSensitive (menu->type_widgets[CASH], False); XtSetSensitive (menu->type_widgets[ASSET], False); - XtSetSensitive (menu->type_widgets[PORTFOLIO], False); + XtSetSensitive (menu->type_widgets[STOCK], False); XtSetSensitive (menu->type_widgets[MUTUAL], False); XtSetSensitive (menu->type_widgets[LIABILITY], False); XtSetSensitive (menu->type_widgets[CREDIT], False); @@ -836,7 +836,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) XtSetSensitive (menu->type_widgets[BANK], False); XtSetSensitive (menu->type_widgets[CASH], False); XtSetSensitive (menu->type_widgets[ASSET], False); - XtSetSensitive (menu->type_widgets[PORTFOLIO], False); + XtSetSensitive (menu->type_widgets[STOCK], False); XtSetSensitive (menu->type_widgets[MUTUAL], False); XtSetSensitive (menu->type_widgets[LIABILITY], False); XtSetSensitive (menu->type_widgets[CREDIT], False); @@ -857,7 +857,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) XtSetSensitive (menu->type_widgets[BANK], False); XtSetSensitive (menu->type_widgets[CASH], False); XtSetSensitive (menu->type_widgets[ASSET], False); - XtSetSensitive (menu->type_widgets[PORTFOLIO], False); + XtSetSensitive (menu->type_widgets[STOCK], False); XtSetSensitive (menu->type_widgets[MUTUAL], False); XtSetSensitive (menu->type_widgets[LIABILITY], False); XtSetSensitive (menu->type_widgets[CREDIT], False); @@ -881,7 +881,7 @@ selectAccountCB( Widget mw, XtPointer cd, XtPointer cb ) XtSetSensitive (menu->type_widgets[ASSET], True); XtSetSensitive (menu->type_widgets[CREDIT], True); XtSetSensitive (menu->type_widgets[LIABILITY], True); - XtSetSensitive (menu->type_widgets[PORTFOLIO], True); + XtSetSensitive (menu->type_widgets[STOCK], True); XtSetSensitive (menu->type_widgets[MUTUAL], True); XtSetSensitive (menu->type_widgets[INCOME], True); XtSetSensitive (menu->type_widgets[EXPENSE], True); diff --git a/src/Account.c b/src/Account.c index b9bd6edbce..12944973b4 100644 --- a/src/Account.c +++ b/src/Account.c @@ -53,6 +53,8 @@ mallocAccount( void ) acc->balance = 0.0; acc->cleared_balance = 0.0; + acc->running_balance = 0.0; + acc->running_cleared_balance = 0.0; acc->flags = 0; acc->type = -1; @@ -61,15 +63,17 @@ mallocAccount( void ) acc->description = NULL; acc->notes = NULL; + /* transction array should be null-terminated */ acc->numTrans = 0; - acc->transaction = NULL; /* Initially there are no transactions - * in this account's transaction - * array */ + acc->transaction = (Transaction **) _malloc (sizeof (Transaction *)); + acc->transaction[0] = NULL; /* private data */ acc->arrowb = NULL; acc->expand = 0; acc->regData = NULL; + acc->regLedger = NULL; + acc->ledgerList = NULL; acc->recnData = NULL; acc->adjBData = NULL; acc->editAccData = NULL; @@ -136,9 +140,13 @@ freeAccount( Account *acc ) acc->arrowb = NULL; acc->expand = 0; acc->regData = NULL; + acc->regLedger = NULL; acc->recnData = NULL; acc->adjBData = NULL; + if (acc->ledgerList) _free (acc->ledgerList); + acc->ledgerList = NULL; + _free(acc); } @@ -154,6 +162,7 @@ xaccGetAccountID (Account *acc) /********************************************************************\ \********************************************************************/ + Transaction * getTransaction( Account *acc, int num ) { @@ -167,6 +176,7 @@ getTransaction( Account *acc, int num ) /********************************************************************\ \********************************************************************/ + int getNumOfTransaction( Account *acc, Transaction *trans ) { @@ -179,6 +189,7 @@ getNumOfTransaction( Account *acc, Transaction *trans ) /********************************************************************\ \********************************************************************/ + Transaction * removeTransaction( Account *acc, int num ) { @@ -198,27 +209,22 @@ removeTransaction( Account *acc, int num ) acc->numTrans--; - if (0 < acc->numTrans) { - acc->transaction = (Transaction **)_malloc((acc->numTrans)* + acc->transaction = (Transaction **)_malloc(((acc->numTrans)+1)* sizeof(Transaction *)); - - trans = oldTrans[acc->numTrans];/* In case we are deleting last in + + trans = oldTrans[acc->numTrans];/* In case we are deleting last in * old array */ - for( i=0,j=0; inumTrans; i++,j++ ) { - if( j != num ) { - acc->transaction[i] = oldTrans[j]; - } else { - trans = oldTrans[j]; - i--; - } - } - } else { - if (0 != num) { - PERR ("removeTransaction(): num should be zero !"); + for( i=0,j=0; inumTrans; i++,j++ ) { + if( j != num ) { + acc->transaction[i] = oldTrans[j]; + } else { + trans = oldTrans[j]; + i--; } - trans = oldTrans[0]; - acc->transaction = NULL; - } + } + + /* make sure the array is NULL terminated */ + acc->transaction[acc->numTrans] = NULL; _free (oldTrans); @@ -244,6 +250,7 @@ xaccRemoveTransaction( Account *acc, Transaction *trans) /********************************************************************\ \********************************************************************/ + int insertTransaction( Account *acc, Transaction *trans ) { @@ -290,47 +297,45 @@ insertTransaction( Account *acc, Transaction *trans ) acc->numTrans++; oldTrans = acc->transaction; - acc->transaction = (Transaction **)_malloc((acc->numTrans)* + acc->transaction = (Transaction **)_malloc(((acc->numTrans) + 1) * sizeof(Transaction *)); - if (oldTrans) { - /* dt is the date of the transaction we are inserting, and dj - * is the date of the "cursor" transaction... we want to insert - * the new transaction before the first transaction of the same - * or later date. The !inserted bit is a bit of a kludge to - * make sure we only insert the new transaction once! */ - dt = &(trans->date); - for( i=0,j=0; inumTrans; i++,j++ ) + /* dt is the date of the transaction we are inserting, and dj + * is the date of the "cursor" transaction... we want to insert + * the new transaction before the first transaction of the same + * or later date. The !inserted bit is a bit of a kludge to + * make sure we only insert the new transaction once! */ + dt = &(trans->date); + for( i=0,j=0; inumTrans; i++,j++ ) + { + /* if we didn't do this, and we needed to insert into the + * last spot in the array, we would walk off the end of the + * old array, which is no good! */ + if( j>=(acc->numTrans-1) ) { - /* if we didn't do this, and we needed to insert into the - * last spot in the array, we would walk off the end of the - * old array, which is no good! */ - if( j>=(acc->numTrans-1) ) + position = i; + acc->transaction[i] = trans; + break; + } + else + { + dj = &(oldTrans[j]->date); + if( (datecmp(dj,dt) > 0) & !inserted ) { position = i; acc->transaction[i] = trans; - break; + j--; + inserted = True; } else - { - dj = &(oldTrans[j]->date); - if( (datecmp(dj,dt) > 0) & !inserted ) - { - position = i; - acc->transaction[i] = trans; - j--; - inserted = True; - } - else - acc->transaction[i] = oldTrans[j]; - } + acc->transaction[i] = oldTrans[j]; } - - _free(oldTrans); - } else { - acc->transaction[0] = trans; - position = 0; - } + } + + /* make sure the array is NULL terminated */ + acc->transaction[acc->numTrans] = NULL; + + _free(oldTrans); if( position != -1 ) qfInsertTransaction( acc->qfRoot, trans ); @@ -340,6 +345,7 @@ insertTransaction( Account *acc, Transaction *trans ) /********************************************************************\ \********************************************************************/ + Account * xaccGetOtherAccount( Account *acc, Transaction *trans ) { @@ -513,6 +519,7 @@ double xaccGetShareBalance (Account *acc, Transaction *trans) * Args: account -- the account for which to recompute balances * * Return: void * \********************************************************************/ + void xaccRecomputeBalance( Account * acc ) { @@ -534,7 +541,7 @@ xaccRecomputeBalance( Account * acc ) share_balance += amt; dbalance += amt * (trans->share_price); - if( trans->reconciled != NREC ) { + if( NREC != trans->reconciled ) { share_cleared_balance += amt; dcleared_balance += amt * (trans->share_price); } @@ -543,7 +550,7 @@ xaccRecomputeBalance( Account * acc ) if (tracc == acc) { /* For bank accounts, the invarient subtotal is the dollar * amount. For stock accoounts, the invarient is the share amount */ - if ( (PORTFOLIO == tracc->type) || ( MUTUAL == tracc->type) ) { + if ( (STOCK == tracc->type) || ( MUTUAL == tracc->type) ) { trans -> credit_share_balance = share_balance; trans -> credit_share_cleared_balance = share_cleared_balance; trans -> credit_balance = trans->share_price * share_balance; @@ -557,7 +564,7 @@ xaccRecomputeBalance( Account * acc ) } tracc = (Account *) trans->debit; if (tracc == acc) { - if ( (PORTFOLIO == tracc->type) || ( MUTUAL == tracc->type) ) { + if ( (STOCK == tracc->type) || ( MUTUAL == tracc->type) ) { trans -> debit_share_balance = share_balance; trans -> debit_share_cleared_balance = share_cleared_balance; trans -> debit_balance = trans->share_price * share_balance; @@ -573,7 +580,7 @@ xaccRecomputeBalance( Account * acc ) last_trans = trans; } - if ( (PORTFOLIO == acc->type) || ( MUTUAL == acc->type) ) { + if ( (STOCK == acc->type) || ( MUTUAL == acc->type) ) { acc -> balance = share_balance * (last_trans->share_price); acc -> cleared_balance = share_cleared_balance * (last_trans->share_price); } else { @@ -594,6 +601,7 @@ xaccRecomputeBalance( Account * acc ) * * Return: int -- non-zero if out of order * \********************************************************************/ + int xaccCheckDateOrder (Account * acc, Transaction *trans ) { @@ -641,6 +649,7 @@ xaccCheckDateOrder (Account * acc, Transaction *trans ) * Args: trans -- the transaction to check * * Return: int -- non-zero if out of order * \********************************************************************/ + int xaccCheckDateOrderDE (Transaction *trans ) { @@ -658,4 +667,62 @@ xaccCheckDateOrderDE (Transaction *trans ) return 0; } -/*************************** end of file **************************** */ +/********************************************************************\ +\********************************************************************/ + +int +xaccIsAccountInList (Account * acc, Account **list) +{ + Account * chk; + int nacc = 0; + int nappearances = 0; + if (!acc) return 0; + if (!list) return 0; + + chk = list[0]; + while (chk) { + if (acc == chk) nappearances ++; + nacc++; + chk = list[nacc]; + } + return nappearances; +} + +/********************************************************************\ +\********************************************************************/ + +void +xaccRecomputeBalances( Account **list ) +{ + Account * acc; + int nacc = 0; + if (!list) return; + + acc = list[0]; + while (acc) { + xaccRecomputeBalance (acc); + nacc++; + acc = list[nacc]; + } +} + +/********************************************************************\ +\********************************************************************/ + +void +xaccZeroRunningBalances( Account **list ) +{ + Account * acc; + int nacc = 0; + if (!list) return; + + acc = list[0]; + while (acc) { + acc -> running_balance = 0.0; + acc -> running_cleared_balance = 0.0; + nacc++; + acc = list[nacc]; + } +} + +/*************************** END OF FILE **************************** */ diff --git a/src/AccountMenu.c b/src/AccountMenu.c index b56418a605..b4bd3eccec 100644 --- a/src/AccountMenu.c +++ b/src/AccountMenu.c @@ -150,6 +150,7 @@ xaccBuildAccountSubMenu (AccountGroup *grp, menuList[i].label = tmp; menuList[i].wclass = &xmPushButtonWidgetClass; menuList[i].mnemonic = 0; + menuList[i].sensitive = True; menuList[i].accelerator = NULL; menuList[i].accel_text = NULL; menuList[i].callback = xaccAccountMenuCB; @@ -172,6 +173,7 @@ xaccBuildAccountSubMenu (AccountGroup *grp, menuList[i+pad].label = tmp; menuList[i+pad].wclass = &xmPushButtonWidgetClass; menuList[i+pad].mnemonic = 0; + menuList[i+pad].sensitive = True; menuList[i+pad].accelerator = NULL; menuList[i+pad].accel_text = NULL; menuList[i+pad].callback = xaccAccountMenuCB; @@ -194,6 +196,7 @@ xaccBuildAccountSubMenu (AccountGroup *grp, menuList[i+pad].label = tmp; menuList[i+pad].wclass = &xmPushButtonWidgetClass; menuList[i+pad].mnemonic = 0; + menuList[i+pad].sensitive = True; menuList[i+pad].accelerator = NULL; menuList[i+pad].accel_text = NULL; menuList[i+pad].callback = xaccAccountMenuCB; diff --git a/src/Action.c b/src/Action.c index 70a40f8b96..fc5a2343ab 100644 --- a/src/Action.c +++ b/src/Action.c @@ -43,6 +43,7 @@ actionBox (Widget parent) AddPopBoxMenuItem (popGUI, "Buy"); AddPopBoxMenuItem (popGUI, "Sell"); AddPopBoxMenuItem (popGUI, "Price"); + AddPopBoxMenuItem (popGUI, "Int"); AddPopBoxMenuItem (popGUI, "Div"); AddPopBoxMenuItem (popGUI, "LTCG"); AddPopBoxMenuItem (popGUI, "STCG"); diff --git a/src/BuildMenu.c b/src/BuildMenu.c index 7f8ecae94b..9cf424c66c 100644 --- a/src/BuildMenu.c +++ b/src/BuildMenu.c @@ -125,6 +125,9 @@ Widget BuildMenu( Widget parent, int menu_type, char *menu_title, NULL ); } + /* possible grey out menu entries */ + XtSetSensitive (widget, items[i].sensitive); + /* If the current widget (menu item) being created corresponds to the * int that the user specified as initial choice, then set initial_choice * = widget. The (int)initial specified by caller of function is the diff --git a/src/Destroy.c b/src/Destroy.c new file mode 100644 index 0000000000..c640f0cf59 --- /dev/null +++ b/src/Destroy.c @@ -0,0 +1,56 @@ +/*******************************************************************\ + * Destroy.c -- utilities for the window destruction (X-Accountant) * + * Copyright (C) 1997 Linas Vepstas * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * * +\********************************************************************/ + +#include "Account.h" +#include "AccWindow.h" +#include "AdjBWindow.h" +#include "Data.h" +#include "LedgerUtils.h" +#include "RegWindow.h" +#include "RecnWindow.h" +#include "util.h" + + +/* ------------------------------------------------------ */ +void +xaccAccountWindowDestroy (Account *acc) +{ + int nacc; + int i, n; + + if (!acc) return; + + /* recursively destroy windows associated with children */ + if (acc->children) { + for (i=0; ichildren->numAcc; i++) { + xaccAccountWindowDestroy (acc->children->account[i]); + } + } + + xaccDestroyRegWindow (acc->regData); + xaccDestroyRegWindow (acc->regLedger); + xaccDestroyRecnWindow (acc->recnData); + xaccDestroyAdjBWindow (acc->adjBData); + xaccDestroyEditAccWindow (acc->editAccData); + xaccDestroyEditNotesWindow (acc->editNotesData); + +} + +/************************** END OF FILE *************************/ diff --git a/src/LedgerUtils.c b/src/LedgerUtils.c new file mode 100644 index 0000000000..069da1085b --- /dev/null +++ b/src/LedgerUtils.c @@ -0,0 +1,323 @@ +/*******************************************************************\ + * LedgerUtils.c -- utilities for the ledger window (X-Accountant) * + * Copyright (C) 1997 Linas Vepstas * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * * +\********************************************************************/ + +#include "Account.h" +#include "Data.h" +#include "Transaction.h" +#include "util.h" + +/* ------------------------------------------------------ */ + +int accListCount (Account **list) +{ + Account *acc; + int nacc = 0; + if (!list) return 0; + + acc = list[0]; + while (acc) { + nacc++; + acc = list[nacc]; + } + return nacc; +} + +/* ------------------------------------------------------ */ + +Account ** accListCopy (Account **list) +{ + Account **newlist; + int i; + int nacc = 0; + + if (!list) return NULL; + nacc = accListCount (list); + if (0 == nacc) return NULL; + + newlist = (Account **) _malloc ((nacc+1) * sizeof (Account *)); + for (i=0; inumTrans; + nacc++; + acc = list[nacc]; + } + ntrans ++; + + /* malloc the array of transactions */ + tarray = (Transaction **) _malloc (ntrans * sizeof (Transaction *)); + + /* put all of the transactions in the flat array */ + nacc = 0; + ntrans = 0; + acc = list[0]; + while (acc) { + for (i=0; inumTrans; i++) { + tarray[ntrans] = getTransaction (acc, i); + ntrans ++; + } + nacc++; + acc = list[nacc]; + } + tarray [ntrans] = NULL; + + /* search and destroy duplicates. */ + /* duplicates are possible due to double-entry */ + /* one transaction can appear at most twice in the list */ + for (i=0; ichildren); + nacc ++; /* add one for this account */ + + list = (Account **) _malloc ((nacc+1) * sizeof (Account *)); + + list[0] = acc; /* must be first -- other code depends on this */ + n = 1; + if (acc->children) { + for (i=0; ichildren->numAcc; i++) { + list[n] = acc->children->account[i]; + + /* recursively add children too */ + if (acc->children->account[i]->children) { + Account **childlist; + Account *childacc; + int ic = 0; + + /* get the children */ + childlist = xaccGroupToList (acc->children->account[i]); + + /* copy them over */ + childacc = childlist[0]; + while (childacc) { + n++; + list[n] = childacc; + childacc = childlist[ic]; + ic ++; + } + _free(childlist); + } + n++; + } + } + list[n] = NULL; + + return list; +} + +/* ------------------------------------------------------ */ + +int +ledgerListCount (struct _RegWindow **list) +{ + struct _RegWindow *reg; + int n; + + if (!list) return 0; + + n = 0; + reg = list[0]; + while (reg) { + n++; + reg = list[n]; + } + return n; +} + +/* ------------------------------------------------------ */ + +void +ledgerListAdd (Account * acc, struct _RegWindow *addreg) +{ + struct _RegWindow **oldlist; + struct _RegWindow **newlist; + struct _RegWindow *reg; + int n; + + if (!acc) return; + if (!addreg) return; + + oldlist = acc->ledgerList; + n = ledgerListCount (oldlist); + + newlist = (struct _RegWindow **) + _malloc ((n+2) * sizeof (struct _RegWindow *)); + + n = 0; + if (oldlist) { + reg = oldlist[0]; + while (reg) { + newlist[n] = reg; + n++; + reg = oldlist[n]; + } + _free (oldlist); + } + newlist[n] = addreg; + newlist[n+1] = NULL; + + acc->ledgerList = newlist; +} + +/* ------------------------------------------------------ */ + +void +ledgerListRemove (Account * acc, struct _RegWindow *delreg) +{ + struct _RegWindow **oldlist; + struct _RegWindow **newlist; + struct _RegWindow *reg; + int n, i; + + if (!acc) return; + if (!delreg) return; + + oldlist = acc->ledgerList; + n = ledgerListCount (oldlist); + + newlist = (struct _RegWindow **) + _malloc ((n+1) * sizeof (struct _RegWindow *)); + + n = 0; + i = 0; + if (oldlist) { + reg = oldlist[0]; + while (reg) { + newlist[i] = reg; + if (delreg == reg) i--; + i++; + n++; + reg = oldlist[n]; + } + _free (oldlist); + } + newlist[i] = NULL; + + acc->ledgerList = newlist; +} + +/* ------------------------------------------------------ */ + +int +ledgerListIsMember (Account * acc, struct _RegWindow *memreg) +{ + struct _RegWindow **list; + struct _RegWindow *reg; + int n; + + if (!acc) return 0; + if (!memreg) return 0; + + list = acc->ledgerList; + if (!list) return 0; + + n = 0; + reg = list[0]; + while (reg) { + if (memreg == reg) return 1; + n++; + reg = list[n]; + } + return 0; +} + +/* ------------------------------------------------------ */ + +void +ledgerListAddList (Account ** list, struct _RegWindow *reg) +{ + Account *acc; + int n = 0; + + if (!list) return; + if (!reg) return; + + acc = list[0]; + while (acc) { + ledgerListAdd (acc, reg); + n++; + acc = list[n]; + } +} + +/* ------------------------------------------------------ */ + +void +ledgerListRemoveList (Account ** list, struct _RegWindow *reg) +{ + Account *acc; + int n = 0; + + if (!list) return; + if (!reg) return; + + acc = list[0]; + while (acc) { + ledgerListRemove (acc, reg); + n++; + acc = list[n]; + } +} + +/************************** END OF FILE *************************/ diff --git a/src/MainWindow.c b/src/MainWindow.c index aa44f9bf1b..892edeba8e 100644 --- a/src/MainWindow.c +++ b/src/MainWindow.c @@ -40,9 +40,11 @@ #include "AccWindow.h" #include "BuildMenu.h" #include "Data.h" +#include "Destroy.h" #include "FileBox.h" #include "FileIO.h" #include "HelpWindow.h" +#include "LedgerUtils.h" #include "main.h" #include "MainWindow.h" #include "RecnWindow.h" @@ -155,7 +157,7 @@ xaccMainWindowAddAcct (Widget acctrix, AccountGroup *grp, int depth ) #endif /* associate a pointer to the actual account with the row */ - XbaeMatrixSetRowUserData ( acctrix, currow, (XtPointer *) acc); + XbaeMatrixSetRowUserData ( acctrix, currow, (XtPointer) acc); /* If the account has sub-accounts, then add an arrow button * next to the account name. Clicking on the arrow button will @@ -354,55 +356,57 @@ mainWindow( Widget parent ) * Set up the menubar * \******************************************************************/ MenuItem fileMenu[] = { - { "New File...", &xmPushButtonWidgetClass, 'N', NULL, NULL, + { "New File...", &xmPushButtonWidgetClass, 'N', NULL, NULL, True, fileMenubarCB, (XtPointer)FMB_NEW, (MenuItem *)NULL }, - { "Open File... ",&xmPushButtonWidgetClass, 'O', NULL, NULL, + { "Open File... ",&xmPushButtonWidgetClass, 'O', NULL, NULL, True, fileMenubarCB, (XtPointer)FMB_OPEN, (MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "Save", &xmPushButtonWidgetClass, 'S', NULL, NULL, + { "Save", &xmPushButtonWidgetClass, 'S', NULL, NULL, True, fileMenubarCB, (XtPointer)FMB_SAVE, (MenuItem *)NULL }, - { "Save As...", &xmPushButtonWidgetClass, 'A', NULL, NULL, + { "Save As...", &xmPushButtonWidgetClass, 'A', NULL, NULL, True, fileMenubarCB, (XtPointer)FMB_SAVEAS,(MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "Quit", &xmPushButtonWidgetClass, 'Q', NULL, NULL, + { "Quit", &xmPushButtonWidgetClass, 'Q', NULL, NULL, True, fileMenubarCB, (XtPointer)FMB_QUIT, (MenuItem *)NULL }, NULL, }; MenuItem accountMenu[] = { - { "New Account...", &xmPushButtonWidgetClass, 'N', NULL, NULL, + { "New Account...", &xmPushButtonWidgetClass, 'N', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_NEW, (MenuItem *)NULL }, - { "Open Account", &xmPushButtonWidgetClass, 'O', NULL, NULL, + { "Open Account", &xmPushButtonWidgetClass, 'O', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_OPEN, (MenuItem *)NULL }, - { "Edit Account...", &xmPushButtonWidgetClass, 'E', NULL, NULL, + { "Open Subaccounts", &xmPushButtonWidgetClass, 'O', NULL, NULL, True, + accountMenubarCB, (XtPointer)AMB_LEDGER, (MenuItem *)NULL }, + { "Edit Account...", &xmPushButtonWidgetClass, 'E', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_EDIT, (MenuItem *)NULL }, - { "Delete Account...", &xmPushButtonWidgetClass, 'D', NULL, NULL, + { "Delete Account...", &xmPushButtonWidgetClass, 'D', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_DEL, (MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "Transfer", &xmPushButtonWidgetClass, 'C', NULL, NULL, + { "Transfer", &xmPushButtonWidgetClass, 'C', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_TRNS, (MenuItem *)NULL }, - { "Report", &xmPushButtonWidgetClass, 'R', NULL, NULL, + { "Report", &xmPushButtonWidgetClass, 'R', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_RPRT, (MenuItem *)NULL }, #if 0 - { "Edit Categories...", &xmPushButtonWidgetClass, 'C', NULL, NULL, + { "Edit Categories...", &xmPushButtonWidgetClass, 'C', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_CAT, (MenuItem *)NULL }, #endif NULL, }; MenuItem helpMenu[] = { - { "About...", &xmPushButtonWidgetClass, 'A', NULL, NULL, + { "About...", &xmPushButtonWidgetClass, 'A', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_ABOUT, (MenuItem *)NULL }, - { "Help...", &xmPushButtonWidgetClass, 'H', NULL, NULL, + { "Help...", &xmPushButtonWidgetClass, 'H', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_MAIN, (MenuItem *)NULL }, - { "Accounts...", &xmPushButtonWidgetClass, 'H', NULL, NULL, + { "Accounts...", &xmPushButtonWidgetClass, 'H', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_ACC, (MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "License...", &xmPushButtonWidgetClass, 'L', NULL, NULL, + { "License...", &xmPushButtonWidgetClass, 'L', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_LIC, (MenuItem *)NULL }, NULL, }; @@ -519,7 +523,7 @@ mainWindow( Widget parent ) listCB, (XtPointer)NULL ); /* If the user double-clicks on an account in the list, open - * up the detail view (ie the regWindow, or whatever) for + * up the detail view (ie the register window, or whatever) for * that type of account */ XtAddCallback( accountlist, XmNdefaultActionCallback, accountMenubarCB, (XtPointer)AMB_OPEN ); @@ -715,7 +719,7 @@ xaccMainWindowRedisplayBalance (void) case BANK: case CASH: case ASSET: - case PORTFOLIO: + case STOCK: case MUTUAL: case CREDIT: case LIABILITY: @@ -919,6 +923,7 @@ accountMenubarCB( Widget mw, XtPointer cd, XtPointer cb ) * which of the file menubar options was chosen * AMB_NEW - New account * AMB_OPEN - Open account + * AMB_LEDGER - Open account and subaccounts in one register * AMB_EDIT - Edit account * AMB_DEL - Delete account * AMB_CAT - Edit catagories @@ -930,9 +935,10 @@ accountMenubarCB( Widget mw, XtPointer cd, XtPointer cb ) DEBUG("AMB_NEW\n"); accWindow(toplevel); break; + case AMB_OPEN: DEBUG("AMB_OPEN\n"); - { + { Account *acc = selected_acc; if( NULL == acc ) { int make_new = verifyBox (toplevel, ACC_NEW_MSG); @@ -940,13 +946,26 @@ accountMenubarCB( Widget mw, XtPointer cd, XtPointer cb ) accWindow(toplevel); } } else { - if( NULL == acc->regData ) { - /* avoid having two registers updating one account */ - acc->regData = regWindow( toplevel, acc ); + regWindowSimple ( toplevel, acc ); + } + } + break; + + case AMB_LEDGER: + DEBUG("AMB_LEDGER\n"); + { + Account *acc = selected_acc; + if( NULL == acc ) { + int make_new = verifyBox (toplevel, ACC_NEW_MSG); + if (make_new) { + accWindow(toplevel); } + } else { + regWindowAccGroup ( toplevel, acc ); } } break; + case AMB_EDIT: DEBUG("AMB_EDIT\n"); { @@ -958,9 +977,10 @@ accountMenubarCB( Widget mw, XtPointer cd, XtPointer cb ) } } break; + case AMB_DEL: DEBUG("AMB_DEL\n"); - { + { Account *acc = selected_acc; if( NULL == acc ) { errorBox (toplevel, ACC_DEL_MSG); @@ -972,14 +992,7 @@ accountMenubarCB( Widget mw, XtPointer cd, XtPointer cb ) /* before deleting the account, make * sure that we close any misc register * windows, if they are open */ - /* hack alert -- this should be done for - * any child accounts this account might have .. */ - xaccDestroyRegWindow (selected_acc->regData); - xaccDestroyRecnWindow (selected_acc->recnData); - xaccDestroyAdjBWindow (selected_acc->adjBData); - xaccDestroyEditAccWindow (selected_acc->editAccData); - xaccDestroyEditNotesWindow (selected_acc->editNotesData); - + xaccAccountWindowDestroy (selected_acc); xaccRemoveAccount (selected_acc); freeAccount (selected_acc); selected_acc = NULL; @@ -988,17 +1001,21 @@ accountMenubarCB( Widget mw, XtPointer cd, XtPointer cb ) } } break; + case AMB_TRNS: DEBUG("AMB_TRNS\n"); xferWindow(toplevel); break; + case AMB_RPRT: DEBUG("AMB_RPRT\n"); simpleReportWindow(toplevel); break; + case AMB_CAT: DEBUG("AMB_CAT\n"); break; + default: PERR ("AccountMenuBarCB(): We shouldn't be here!\n"); } diff --git a/src/Makefile b/src/Makefile index 73f4d91fad..4854999f76 100644 --- a/src/Makefile +++ b/src/Makefile @@ -27,8 +27,8 @@ # DO NOT EDIT THE STUFF BELOW THIS LINE! # OBJS = Account.o AccountMenu.o AccWindow.o Action.o AdjBWindow.o \ - BuildMenu.o Data.o date.o \ - FileBox.o FileIO.o HelpWindow.o main.o MainWindow.o PopBox.o \ + BuildMenu.o Data.o date.o Destroy.o \ + FileBox.o FileIO.o HelpWindow.o LedgerUtils.o main.o MainWindow.o PopBox.o \ QIFIO.o QuickFill.o RecnWindow.o RegWindow.o Reports.o \ TextBox.o Transaction.o util.o XferBox.o XferWindow.o diff --git a/src/PopBox.c b/src/PopBox.c index 243c6702b0..0c23fd1250 100644 --- a/src/PopBox.c +++ b/src/PopBox.c @@ -33,7 +33,7 @@ typedef struct _PopBox { /** PROTOTYPES ******************************************************/ -void selectCB (Widget w, XtPointer cd, XtPointer cb ); +static void selectCB (Widget w, XtPointer cd, XtPointer cb ); /********************************************************************\ * popBox * @@ -68,8 +68,10 @@ popBox (Widget parent) XmNvalue, "", /* hack alert -- the width of the combobox should be relative to the font, should - be relative to the size of the cell in which it will fit. */ + * be relative to the size of the cell in which it will fit. Basically, these + * values should not be hard-coded, but should be conmputed somehow */ XmNwidth, 53, + XmNdropDownWidth, 103, NULL); popData -> combobox = combobox; @@ -133,11 +135,15 @@ void SetPopBox (PopBox *ab, int row, int col) choice = XbaeMatrixGetCell (ab->reg, ab->currow, ab->curcol); /* do a menu selection only if the cell ain't empty. */ - if (0x0 != choice[0]) { - /* convert String to XmString ... arghhh */ - choosen = XmCvtCTToXmString (choice); - XmComboBoxSelectItem (ab->combobox, choosen, False); - XmStringFree (choosen); + if (choice) { + if (0x0 != choice[0]) { + /* convert String to XmString ... arghhh */ + choosen = XmCvtCTToXmString (choice); + XmComboBoxSelectItem (ab->combobox, choosen, False); + XmStringFree (choosen); + } else { + XmComboBoxClearItemSelection (ab->combobox); + } } else { XmComboBoxClearItemSelection (ab->combobox); } @@ -156,6 +162,17 @@ void SetPopBox (PopBox *ab, int row, int col) } } +/********************************************************************\ +\********************************************************************/ + +void freePopBox (PopBox *ab) +{ + if (!ab) return; + SetPopBox (ab, -1, -1); + XtDestroyWidget (ab->combobox); + _free (ab); +} + /********************************************************************\ * selectCB -- get the user's selection, put the string into the * * cell. * @@ -166,7 +183,7 @@ void SetPopBox (PopBox *ab, int row, int col) * Return: none * \********************************************************************/ -void selectCB (Widget w, XtPointer cd, XtPointer cb ) +static void selectCB (Widget w, XtPointer cd, XtPointer cb ) { PopBox *ab = (PopBox *) cd; @@ -182,4 +199,5 @@ void selectCB (Widget w, XtPointer cd, XtPointer cb ) /* a diffeent way of getting the user's selection ... */ /* text = XmComboBoxGetString (ab->combobox); */ } + /************************* END OF FILE ******************************/ diff --git a/src/QIFIO.c b/src/QIFIO.c index 72a5816f03..fbaee3bada 100644 --- a/src/QIFIO.c +++ b/src/QIFIO.c @@ -156,7 +156,7 @@ char * xaccReadQIFAccount (int fd, Account * acc) acc -> type = BANK; } else if (!strcmp (&qifline[1], "Invst\r\n")) { - acc -> type = PORTFOLIO; + acc -> type = STOCK; } else { DEBUG ("Unsupported account type\n"); DEBUG (&qifline[1]); diff --git a/src/RegWindow.c b/src/RegWindow.c index 8b19e67585..7a72a44c72 100644 --- a/src/RegWindow.c +++ b/src/RegWindow.c @@ -42,44 +42,83 @@ #include "date.h" #include "main.h" #include "MainWindow.h" +#include "LedgerUtils.h" #include "PopBox.h" #include "QuickFill.h" #include "RecnWindow.h" #include "Transaction.h" #include "util.h" + +#define NUM_COLUMNS 20 +#define NUM_HEADER_ROWS 1 /* also works if #define to 2 */ +#define NUM_ROWS_PER_TRANS 2 + +/* enumerate different ledger types */ +enum { + GEN_LEDGER = NUM_ACCOUNT_TYPES, + INC_LEDGER = NUM_ACCOUNT_TYPES + 1, + PORTFOLIO = NUM_ACCOUNT_TYPES + 2, +}; + + /** STRUCTS *********************************************************/ /* The RegWindow struct contains info needed by an instance of an open * register. Any state info for the regWindow goes here. */ typedef struct _RegWindow { - Account *acc; /* The account associated with this regwin */ + Account **blackacc; /* The list of accounts associated with this regwin */ + short numAcc; /* number of accounts in list */ + + /* display widgets */ Widget dialog; Widget reg; /* The matrix widget... */ Widget balance; /* The balance text field */ unsigned short changed; /* bitmask of fields that have changed in * - * transaction lastTrans */ - unsigned short lastTrans; /* to keep track of last edited transaction*/ + * transaction currEntry */ + unsigned short currEntry; /* to keep track of last edited transaction*/ + + char type; /* register display type, usually equal to * + * account type */ + + /* quick-fill stuff */ XmTextPosition insert; /* used by quickfill for detecting deletes */ QuickFill *qf; /* keeps track of current quickfill node. * * Reset to Account->qfRoot when entering * * a new transaction */ + + /* pull-down (combo) box stuff */ PopBox *actbox; /* ComboBox for actions */ - PopBox *xferbox; /* ComboBox for transfers */ + PopBox *xfrmbox; /* ComboBox for transfers */ + PopBox *xtobox; /* ComboBox for transfers */ + + /* structures for controlling the column layout */ + short numCols; /* number of columns in the register */ + + short cellColLocation [NUM_COLUMNS]; /* cell location, column */ + short cellRowLocation [NUM_COLUMNS]; /* cell location, row */ + short columnWidths [NUM_COLUMNS]; /* widths (in chars not pixels) */ + + String columnLabels [NUM_HEADER_ROWS + NUM_ROWS_PER_TRANS] + [NUM_COLUMNS]; /* column labels */ + unsigned char alignments[NUM_COLUMNS]; /* alignment of display chars */ + } RegWindow; /** PROTOTYPES ******************************************************/ -double regRecalculateBalance( RegWindow *regData ); -void regSaveTransaction( RegWindow *regData, int position ); +RegWindow * regWindowLedger( Widget parent, Account **acclist, int type); -void closeRegWindow( Widget mw, XtPointer cd, XtPointer cb ); -void startRecnCB( Widget mw, XtPointer cd, XtPointer cb ); -void startAdjBCB( Widget mw, XtPointer cd, XtPointer cb ); -void recordCB( Widget mw, XtPointer cd, XtPointer cb ); -void deleteCB( Widget mw, XtPointer cd, XtPointer cb ); -void cancelCB( Widget mw, XtPointer cd, XtPointer cb ); -void regCB( Widget mw, XtPointer cd, XtPointer cb ); -void dateCellFormat( Widget mw, XbaeMatrixModifyVerifyCallbackStruct *mvcbs, int do_year ); +static double regRecalculateBalance( RegWindow *regData ); +static void regSaveTransaction( RegWindow *regData, int position ); + +static void closeRegWindow( Widget mw, XtPointer cd, XtPointer cb ); +static void startRecnCB( Widget mw, XtPointer cd, XtPointer cb ); +static void startAdjBCB( Widget mw, XtPointer cd, XtPointer cb ); +static void recordCB( Widget mw, XtPointer cd, XtPointer cb ); +static void deleteCB( Widget mw, XtPointer cd, XtPointer cb ); +static void cancelCB( Widget mw, XtPointer cd, XtPointer cb ); +static void regCB( Widget mw, XtPointer cd, XtPointer cb ); +static void dateCellFormat( Widget mw, XbaeMatrixModifyVerifyCallbackStruct *mvcbs, int do_year ); /** GLOBALS *********************************************************/ @@ -107,69 +146,125 @@ extern Pixel negPixel; #define MOD_PRIC 0x40 #define MOD_MEMO 0x80 #define MOD_ACTN 0x100 -#define MOD_XFER 0x200 -#define MOD_NEW 0x400 -#define MOD_ALL 0x7ff +#define MOD_XFRM 0x200 +#define MOD_XTO 0x400 +#define MOD_NEW 0x800 +#define MOD_ALL 0xfff /* These defines are indexes into the column location array */ -#define DATE_COL_ID 0 -#define NUM_COL_ID 1 -#define DESC_COL_ID 2 -#define RECN_COL_ID 3 -#define PAY_COL_ID 4 -#define DEP_COL_ID 5 -#define PRIC_COL_ID 6 -#define SHRS_COL_ID 7 -#define BALN_COL_ID 8 -#define ACTN_COL_ID 9 - -/* the actual column location is pulled out of the column location array */ -#define DATE_CELL_R 0 -#define DATE_CELL_C (acc->columnLocation[DATE_COL_ID]) -#define NUM_CELL_R 0 -#define NUM_CELL_C (acc->columnLocation[NUM_COL_ID]) -#define DESC_CELL_R 0 -#define DESC_CELL_C (acc->columnLocation[DESC_COL_ID]) -#define RECN_CELL_R 0 -#define RECN_CELL_C (acc->columnLocation[RECN_COL_ID]) -#define PAY_CELL_R 0 -#define PAY_CELL_C (acc->columnLocation[PAY_COL_ID]) -#define DEP_CELL_R 0 -#define DEP_CELL_C (acc->columnLocation[DEP_COL_ID]) -#define BALN_CELL_R 0 -#define BALN_CELL_C (acc->columnLocation[BALN_COL_ID]) - -#define PRIC_CELL_C (acc->columnLocation[PRIC_COL_ID]) -#define SHRS_CELL_C (acc->columnLocation[SHRS_COL_ID]) -#define ACTN_CELL_C (acc->columnLocation[ACTN_COL_ID]) - -#define YEAR_CELL_R 1 -#define YEAR_CELL_C DATE_CELL_C /* same column as the date */ -#define XFER_CELL_R 1 -#define XFER_CELL_C NUM_CELL_C /* same column as the transaction number */ -#define MEMO_CELL_R 1 -#define MEMO_CELL_C DESC_CELL_C /* same column as the description */ +#define DATE_CELL_ID 0 +#define YEAR_CELL_ID 1 +#define NUM_CELL_ID 2 +#define ACTN_CELL_ID 3 +#define XFRM_CELL_ID 4 +#define XTO_CELL_ID 5 +#define DESC_CELL_ID 6 +#define MEMO_CELL_ID 7 +#define RECN_CELL_ID 8 +#define PAY_CELL_ID 9 /* amount debit */ +#define DEP_CELL_ID 10 /* amount credit */ +#define PRCC_CELL_ID 11 /* price credit */ +#define PRCD_CELL_ID 12 /* price debit */ +#define SHRS_CELL_ID 13 +#define BALN_CELL_ID 14 +#define VCRD_CELL_ID 15 /* value credit */ +#define VDEB_CELL_ID 16 /* value debit */ + +/* the actual cell location is pulled out of the location array */ +#define DATE_CELL_C (regData->cellColLocation[DATE_CELL_ID]) +#define YEAR_CELL_C (regData->cellColLocation[YEAR_CELL_ID]) +#define NUM_CELL_C (regData->cellColLocation[NUM_CELL_ID]) +#define ACTN_CELL_C (regData->cellColLocation[ACTN_CELL_ID]) +#define DESC_CELL_C (regData->cellColLocation[DESC_CELL_ID]) +#define MEMO_CELL_C (regData->cellColLocation[MEMO_CELL_ID]) +#define XFRM_CELL_C (regData->cellColLocation[XFRM_CELL_ID]) +#define XTO_CELL_C (regData->cellColLocation[XTO_CELL_ID]) +#define RECN_CELL_C (regData->cellColLocation[RECN_CELL_ID]) +#define PAY_CELL_C (regData->cellColLocation[PAY_CELL_ID]) +#define DEP_CELL_C (regData->cellColLocation[DEP_CELL_ID]) +#define PRCC_CELL_C (regData->cellColLocation[PRCC_CELL_ID]) +#define PRCD_CELL_C (regData->cellColLocation[PRCD_CELL_ID]) +#define SHRS_CELL_C (regData->cellColLocation[SHRS_CELL_ID]) +#define BALN_CELL_C (regData->cellColLocation[BALN_CELL_ID]) +#define VCRD_CELL_C (regData->cellColLocation[VCRD_CELL_ID]) +#define VDEB_CELL_C (regData->cellColLocation[VDEB_CELL_ID]) + +#define DATE_CELL_R (regData->cellRowLocation[DATE_CELL_ID]) +#define YEAR_CELL_R (regData->cellRowLocation[YEAR_CELL_ID]) +#define NUM_CELL_R (regData->cellRowLocation[NUM_CELL_ID]) +#define ACTN_CELL_R (regData->cellRowLocation[ACTN_CELL_ID]) +#define DESC_CELL_R (regData->cellRowLocation[DESC_CELL_ID]) +#define MEMO_CELL_R (regData->cellRowLocation[MEMO_CELL_ID]) +#define XFRM_CELL_R (regData->cellRowLocation[XFRM_CELL_ID]) +#define XTO_CELL_R (regData->cellRowLocation[XTO_CELL_ID]) +#define RECN_CELL_R (regData->cellRowLocation[RECN_CELL_ID]) +#define PAY_CELL_R (regData->cellRowLocation[PAY_CELL_ID]) +#define DEP_CELL_R (regData->cellRowLocation[DEP_CELL_ID]) +#define PRCC_CELL_R (regData->cellRowLocation[PRCC_CELL_ID]) +#define PRCD_CELL_R (regData->cellRowLocation[PRCD_CELL_ID]) +#define SHRS_CELL_R (regData->cellRowLocation[SHRS_CELL_ID]) +#define BALN_CELL_R (regData->cellRowLocation[BALN_CELL_ID]) +#define VCRD_CELL_R (regData->cellRowLocation[VCRD_CELL_ID]) +#define VDEB_CELL_R (regData->cellRowLocation[VDEB_CELL_ID]) + /** CELL MACROS *****************************************************/ -#define IN_DATE_CELL(R,C) (((R-1)%2==0) && (C==DATE_CELL_C)) /* Date cell */ -#define IN_NUM_CELL(R,C) (((R-1)%2==0) && (C==NUM_CELL_C)) /* Number cell */ -#define IN_DESC_CELL(R,C) (((R-1)%2==0) && (C==DESC_CELL_C)) /* Description cell */ -#define IN_RECN_CELL(R,C) (((R-1)%2==0) && (C==RECN_CELL_C)) /* Reconciled cell */ -#define IN_PAY_CELL(R,C) (((R-1)%2==0) && (C==PAY_CELL_C)) /* Payment cell */ -#define IN_DEP_CELL(R,C) (((R-1)%2==0) && (C==DEP_CELL_C)) /* Deposit cell */ -#define IN_BALN_CELL(R,C) (((R-1)%2==0) && (C==BALN_CELL_C)) /* Balance cell */ -#define IN_PRIC_CELL(R,C) (((R-1)%2==0) && (C==PRIC_CELL_C)) /* Price cell */ -#define IN_ACTN_CELL(R,C) (((R-1)%2==0) && (C==ACTN_CELL_C)) /* Action cell */ - -#define IN_YEAR_CELL(R,C) (((R-1)%2==1) && (C==DATE_CELL_C)) /* Year cell */ -#define IN_XFER_CELL(R,C) (((R-1)%2==1) && (C==XFER_CELL_C)) /* Transfer cell */ -#define IN_MEMO_CELL(R,C) (((R-1)%2==1) && (C==MEMO_CELL_C)) /* Memo cell */ -#define IN_BAD_CELL(R,C) (((R-1)%2==1) && (C!=MEMO_CELL_C)) /* Not the memo cell*/ +#define RMOD(R,RM) ((R-NUM_HEADER_ROWS)%NUM_ROWS_PER_TRANS==RM) + + /* Date cell */ +#define IN_DATE_CELL(R,C) (RMOD(R,DATE_CELL_R) && (C==DATE_CELL_C)) + + /* Number cell */ +#define IN_NUM_CELL(R,C) (RMOD(R,NUM_CELL_R) && (C==NUM_CELL_C)) + + /* Description cell */ +#define IN_DESC_CELL(R,C) (RMOD(R,DESC_CELL_R) && (C==DESC_CELL_C)) + + /* Reconciled cell */ +#define IN_RECN_CELL(R,C) (RMOD(R,RECN_CELL_R) && (C==RECN_CELL_C)) + + /* Payment cell */ +#define IN_PAY_CELL(R,C) (RMOD(R,PAY_CELL_R) && (C==PAY_CELL_C)) + + /* Deposit cell */ +#define IN_DEP_CELL(R,C) (RMOD(R,DEP_CELL_R) && (C==DEP_CELL_C)) + + /* Price cell */ +#define IN_PRCC_CELL(R,C) (RMOD(R,PRCC_CELL_R) && (C==PRCC_CELL_C)) +#define IN_PRCD_CELL(R,C) (RMOD(R,PRCD_CELL_R) && (C==PRCD_CELL_C)) + + /* Action cell */ +#define IN_ACTN_CELL(R,C) (RMOD(R,ACTN_CELL_R) && (C==ACTN_CELL_C)) + + /* Year cell */ +#define IN_YEAR_CELL(R,C) (RMOD(R,YEAR_CELL_R) && (C==YEAR_CELL_C)) + + /* Transfer cell */ +#define IN_XFRM_CELL(R,C) (RMOD(R,XFRM_CELL_R) && (C==XFRM_CELL_C)) +#define IN_XTO_CELL(R,C) (RMOD(R,XTO_CELL_R) && (C==XTO_CELL_C)) + + /* Balance cell */ +#define IN_BALN_CELL(R,C) (RMOD(R,BALN_CELL_R) && (C==BALN_CELL_C)) + + /* Transaction Value credit cell */ +#define IN_VCRD_CELL(R,C) (RMOD(R,VCRD_CELL_R) && (C==VCRD_CELL_C)) + + /* Transaction Value debit cell */ +#define IN_VDEB_CELL(R,C) (RMOD(R,VDEB_CELL_R) && (C==VDEB_CELL_C)) + + /* Memo cell */ +#define IN_MEMO_CELL(R,C) (RMOD(R,MEMO_CELL_R) && (C==MEMO_CELL_C)) + + /* Not the memo cell*/ +#define IN_BAD_CELL(R,C) (RMOD(R,1) && (C!=MEMO_CELL_C)) /********************************************************************/ /********************************************************************\ + * xaccDestroyRegWindow() + * It is enought to call just XtDestroy Widget. Any allocated + * memory will be freed by the close callbacks. \********************************************************************/ void @@ -193,80 +288,139 @@ regRefresh( RegWindow *regData ) if( regData != NULL ) { Transaction *trans; - int i,j,nrows,drows,nnrows,ncols; + Transaction **tarray; + int old_num_rows, new_num_rows, delta_rows; + int i,j, ntrans, ncols; char buf[BUFSIZE]; String **data = NULL; String **newData; - Account *acc, *xfer_acc; double themount; /* amount */ - XtVaGetValues( regData->reg, XmNrows, &nrows, NULL ); + /* first, build a sorted array of transactions */ + if (1 == regData->numAcc) { + tarray = regData->blackacc[0]->transaction; + ntrans = regData->blackacc[0]->numTrans; + + } else { + tarray = accListGetSortedTrans (regData->blackacc); + ntrans = xaccCountTransactions (tarray); + } + + /* Allocate one extra transaction row. That extra row + * is used to allow the user to add new transactions */ + new_num_rows = NUM_ROWS_PER_TRANS*(ntrans+1) + NUM_HEADER_ROWS; + + XtVaGetValues( regData->reg, XmNrows, &old_num_rows, NULL ); XtVaGetValues( regData->reg, XmNcells, &data, NULL ); /* The number of rows we need to add/subtract (ie, delta-rows :) */ - nnrows = (regData->acc->numTrans)*2 + 3; - drows = (nnrows-1) - (nrows-1); - ncols = regData->acc->numCols; - acc = regData->acc; + delta_rows = new_num_rows - old_num_rows; + ncols = regData->numCols; + /* allocate a new matrix: */ - newData = (String **)_malloc(nnrows*sizeof(String *)); - for( i=0; ireg, 1, -drows ); - else if( drows > 0 ) - XbaeMatrixAddRows( regData->reg, 1, NULL, NULL, NULL, drows ); + if( delta_rows < 0 ) { + XbaeMatrixDeleteRows( regData->reg, NUM_HEADER_ROWS, -delta_rows ); + } else if( delta_rows > 0 ) { + XbaeMatrixAddRows( regData->reg, NUM_HEADER_ROWS, + NULL, NULL, NULL, delta_rows ); + } /* and fill in the data for the matrix: */ - i=-1; - while( (trans = getTransaction (acc,++i)) != NULL ) - { - int row = i*2+1; - - sprintf( buf, "%2d/%2d\0", - trans->date.month, - trans->date.day ); - newData[row][DATE_CELL_C] = XtNewString(buf); + for (i=0; ireg, row, (XPointer) trans); + + sprintf( buf, "%2d/%2d", trans->date.month, trans->date.day ); + newData[row+DATE_CELL_R][DATE_CELL_C] = XtNewString(buf); sprintf( buf, "%4d", trans->date.year ); - newData[row+1][DATE_CELL_C] = XtNewString(buf); /* YEAR_CELL_C */ + newData[row+YEAR_CELL_R][YEAR_CELL_C] = XtNewString(buf); sprintf( buf, "%s", trans->num ); - newData[row][NUM_CELL_C] = XtNewString(buf); + newData[row+NUM_CELL_R][NUM_CELL_C] = XtNewString(buf); + + /* for a general ledger, we fill out the "from" and "to" + * transfer fields */ + if ( (GEN_LEDGER == regData->type) || + (INC_LEDGER == regData->type) || + (PORTFOLIO == regData->type) ) { + Account *xfer_acc; + xfer_acc = (Account *) trans -> debit; + if (xfer_acc) { + sprintf( buf, "%s", xfer_acc->accountName ); + newData[row+XFRM_CELL_R][XFRM_CELL_C] = XtNewString(buf); + } + xfer_acc = (Account *) trans -> credit; + if (xfer_acc) { + sprintf( buf, "%s", xfer_acc->accountName ); + newData[row+XTO_CELL_R][XTO_CELL_C] = XtNewString(buf); + } - /* XFER_CELL_C is same as NUM_CELL_C */ - xfer_acc = xaccGetOtherAccount (acc, trans); - if (xfer_acc) { - sprintf( buf, "%s", xfer_acc->accountName ); - newData[row+1][NUM_CELL_C] = XtNewString(buf); } else { - newData[row+1][NUM_CELL_C] = XtNewString(""); + + /* if here, then this is not a general ledger type display. + * We are displaying just one account. */ + Account *main_acc, *xfer_acc; + + main_acc = regData->blackacc[0]; + xfer_acc = xaccGetOtherAccount (main_acc, trans); + if (xfer_acc) { + sprintf( buf, "%s", xfer_acc->accountName ); + newData[row+XFRM_CELL_R][XFRM_CELL_C] = XtNewString(buf); + } } - - + sprintf( buf, "%s", trans->description ); - newData[row][DESC_CELL_C] = XtNewString(buf); + newData[row+DESC_CELL_R][DESC_CELL_C] = XtNewString(buf); sprintf( buf, "%s", trans->memo ); - newData[row+1][DESC_CELL_C] = XtNewString(buf); + newData[row+MEMO_CELL_R][MEMO_CELL_C] = XtNewString(buf); sprintf( buf, "%c", trans->reconciled ); - newData[row][RECN_CELL_C] = XtNewString(buf); - newData[row+1][RECN_CELL_C] = XtNewString(""); - + newData[row+RECN_CELL_R][RECN_CELL_C] = XtNewString(buf); + + sprintf( buf, "%s", trans->action ); + newData[row+ACTN_CELL_R][ACTN_CELL_C] = XtNewString(buf); + /* ----------------------------------- */ - /* display depends on account type */ - switch(acc->type) - { + /* display of amounts depends on account type */ + /* For-single-account displays, positive and negative + * quantities are sorted into "payment" and "deposit" + * columns. The value in each column is always positive. + * + * For a general ledger display, the sign is preserved, + * and the two columns are interpreted as "debit" and + * "credit" columns. + * + * Note also, that for single accounts, the amount + * is fetched based on the account, since this may + * introduce and extra minus sign, depending on whether + * the account is being debited instead of credited. + * + * For general ledger entries, we can fetch the amount + * directly: we already know which accounts should be + * debited and credited. + */ + switch (regData->type) { case BANK: case CASH: case ASSET: @@ -274,51 +428,141 @@ regRefresh( RegWindow *regData ) case LIABILITY: case INCOME: case EXPENSE: - case EQUITY: - themount = xaccGetAmount (acc, trans); - if( 0.0 > themount ) - { + case EQUITY: { + Account *main_acc = regData->blackacc[0]; + themount = xaccGetAmount (main_acc, trans); + if( 0.0 > themount ) { sprintf( buf, "%.2f ", -themount ); - newData[row][PAY_CELL_C] = XtNewString(buf); - newData[row][DEP_CELL_C] = XtNewString(""); - } - else - { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(buf); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } else { sprintf( buf, "%.2f ", themount ); - newData[row][PAY_CELL_C] = XtNewString(""); - newData[row][DEP_CELL_C] = XtNewString(buf); - } + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(buf); + } + } break; - case PORTFOLIO: - case MUTUAL: - themount = xaccGetShareAmount (acc, trans); - if( 0.0 > themount ) - { + + case STOCK: + case MUTUAL: { + Account *main_acc = regData->blackacc[0]; + themount = xaccGetShareAmount (main_acc, trans); + + /* if the share amount is zero (e.g. for a price quote) + * then just leave both these cells blank */ + if( DEQ (0.0,themount) ) { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } else + if( 0.0 > themount ) { sprintf( buf, "%.3f ", -themount ); - newData[row][PAY_CELL_C] = XtNewString(buf); - newData[row][DEP_CELL_C] = XtNewString(""); - } - else - { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(buf); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } else { sprintf( buf, "%.3f ", themount ); - newData[row][PAY_CELL_C] = XtNewString(""); - newData[row][DEP_CELL_C] = XtNewString(buf); - } + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(buf); + } + } break; + + case INC_LEDGER: + case GEN_LEDGER: { + Account * acc; + int show; + themount = trans->damount * trans->share_price; + + /* attempt to keep all displayed quantities positive by + * flipping signs and columns for negative entries */ + /* hack alert -- but does this really work ??? */ + if (0.0 < themount) { + sprintf( buf, "%.2f ", themount ); + acc = (Account *) (trans->debit); + show = xaccIsAccountInList (acc, regData->blackacc); + if (show) { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(buf); + } else { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + } + + acc = (Account *) (trans->credit); + show = xaccIsAccountInList (acc, regData->blackacc); + if (show) { + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(buf); + } else { + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } + } else { + themount = - themount; + sprintf( buf, "%.2f ", themount ); + acc = (Account *) (trans->debit); + show = xaccIsAccountInList (acc, regData->blackacc); + if (show) { + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(buf); + } else { + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } + + acc = (Account *) (trans->credit); + show = xaccIsAccountInList (acc, regData->blackacc); + if (show) { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(buf); + } else { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + } + } + } + break; + + case PORTFOLIO: { + Account * acc; + int show; + + /* Show the share debit amount, if the debit account + * is in this ledger. But don't show share amount unless + * the account type is stock or mutual, since other types + * do not have shares. */ + show = 0; + acc = (Account *) (trans->debit); + if (acc) { + if ((MUTUAL == acc->type) || (STOCK == acc->type) ) { + show += xaccIsAccountInList (acc, regData->blackacc); + } + } + acc = (Account *) (trans->credit); + if (acc) { + if ((MUTUAL == acc->type) || (STOCK == acc->type) ) { + show += xaccIsAccountInList (acc, regData->blackacc); + } + } + + /* if the amount is zero, then leave the cell blank */ + themount = trans->damount; + if (show && !(DEQ(0.0, themount)) ) { + sprintf( buf, "%.3f ", DABS(themount) ); + + if (0.0 > themount ) { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(buf); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } else { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(buf); + } + } else { + newData[row+PAY_CELL_R][PAY_CELL_C] = XtNewString(""); + newData[row+DEP_CELL_R][DEP_CELL_C] = XtNewString(""); + } + } + break; + default: - fprintf( stderr, "Internal Error: Account type: %d is unknown!\n", acc->type); - } - - newData[row+1][PAY_CELL_C] = XtNewString(""); - newData[row+1][DEP_CELL_C] = XtNewString(""); + fprintf( stderr, "Internal Error: Account type: %d is unknown!\n", + regData->type); + } - newData[row][BALN_CELL_C] = XtNewString(""); - newData[row+1][BALN_CELL_C] = XtNewString(""); - /* ----------------------------------- */ /* extra columns for mutual funds, etc. */ - switch(acc->type) - { + switch(regData->type) { case BANK: case CASH: case ASSET: @@ -327,66 +571,109 @@ regRefresh( RegWindow *regData ) case INCOME: case EXPENSE: case EQUITY: + case GEN_LEDGER: + case INC_LEDGER: break; - case PORTFOLIO: + case STOCK: case MUTUAL: sprintf( buf, "%.2f ", trans->share_price ); - newData[row][PRIC_CELL_C] = XtNewString(buf); - newData[row+1][PRIC_CELL_C] = XtNewString(""); + newData[row+PRCC_CELL_R][PRCC_CELL_C] = XtNewString(buf); + break; - /* don't set number of shares here -- this is computed later, - * in recomputeBalance. */ - newData[row][SHRS_CELL_C] = XtNewString(""); - newData[row+1][SHRS_CELL_C] = XtNewString(""); + case PORTFOLIO: { + Account * acc; + int show; + + /* show the price, only if the transaction records a + * share purchase/sale. Otherwise, its a plain dollar + * amount, and the price should not be shown. */ + show = 0; + acc = (Account *) (trans->debit); + if (acc) { + if ( (MUTUAL == acc->type) || (STOCK == acc->type) ) { + show += xaccIsAccountInList (acc, regData->blackacc); + } + } + acc = (Account *) (trans->credit); + if (acc) { + if ( (MUTUAL == acc->type) || (STOCK == acc->type) ) { + show += xaccIsAccountInList (acc, regData->blackacc); + } + } - sprintf( buf, "%s", trans->action ); - newData[row][ACTN_CELL_C] = XtNewString(buf); - newData[row+1][ACTN_CELL_C] = XtNewString(""); - + /* row location of prices depends on whether this + * is a purchase or a sale */ + if (show) { + sprintf( buf, "%.3f ", trans->share_price ); + themount = trans->damount; + if (0.0 < themount) { + newData[row+PRCC_CELL_R][PRCC_CELL_C] = XtNewString(buf); + } else { + newData[row+PRCD_CELL_R][PRCD_CELL_C] = XtNewString(buf); + } + } else { + newData[row+PRCC_CELL_R][PRCC_CELL_C] = XtNewString(""); + newData[row+PRCD_CELL_R][PRCD_CELL_C] = XtNewString(""); + } + } break; default: - fprintf( stderr, "Internal Error: Account type: %d is unknown!\n", acc->type); - } + break; } + } - /* fill in the empty cells at the end: */ { - Date date; - todaysDate( &date ); + int row; + Date date; + todaysDate( &date ); - for( i=(2*i)+1; ireg, row, NULL); + + sprintf( buf, "%2d/%2d", date.month, date.day ); + newData[row+DATE_CELL_R][DATE_CELL_C] = XtNewString(buf); + sprintf( buf, "%4d", date.year ); + newData[row+YEAR_CELL_R][YEAR_CELL_C] = XtNewString(buf); + + sprintf( buf, "%c", NREC); + newData[row+RECN_CELL_R][RECN_CELL_C] = XtNewString(buf); + + } + + /* seems that Xbae doesn't like null cell entries, so fill them up. */ + for( i=0; ireg, XmNcells, newData, NULL ); regRecalculateBalance (regData); + /* If the number of accounts is greater than one, + * then the flat transaction array was alloc'ed and + * must be freed. */ + if (1 != regData->numAcc) { + _free (tarray); + } + /* and free memory!!! */ - /* ??? */ + /* note that the XbaeMatrix widget makes a copy of everything, + * so it is not only safe, but necessary to free everything. + */ + for( i=0; ireg; - acc = regData->acc; - xaccRecomputeBalance (acc); - - for( i=0; (trans=getTransaction(acc,i)) != NULL; i++ ) - { - dbalance = xaccGetBalance (acc, trans); - dclearedBalance = xaccGetClearedBalance (acc, trans); - share_balance = xaccGetShareBalance (acc, trans); + /* recompute the balances for each of the accounts involved */ + xaccRecomputeBalances (regData->blackacc); + + /* get the number of rows in the register */ + XtVaGetValues( regData->reg, XmNrows, &num_rows, NULL ); + + /* subtract the last (not-yet-existant) transaction */ + num_rows -= NUM_ROWS_PER_TRANS; + + /* zero everything out */ + dbalance = 0.0; + dclearedBalance = 0.0; + xaccZeroRunningBalances (regData->blackacc); + /* loop over all of the rows */ + for (position = NUM_HEADER_ROWS; position < num_rows; + position += NUM_ROWS_PER_TRANS) { + + trans = (Transaction *) XbaeMatrixGetRowUserData (reg, position); + if (!trans) { + PERR ("regRecalculateBalance(): missing transaction\n"); + continue; + } + + credit_acc = (Account *) (trans->credit); + debit_acc = (Account *) (trans->debit); + + /* figure out if this transaction shows up as a debit + * and/or a credit for this list of accounts. Note + * that it may show up as both, and that's OK. */ + is_credit = xaccIsAccountInList (credit_acc, regData -> blackacc ); + is_debit = xaccIsAccountInList (debit_acc, regData -> blackacc ); + + /* increment and/or decrement the running balance as appropriate */ + if (is_credit) { + tmp = xaccGetBalance (credit_acc, trans); + dbalance -= credit_acc -> running_balance; + dbalance += tmp; + credit_acc -> running_balance = tmp; + + if( NREC != trans->reconciled ) { + tmp = xaccGetClearedBalance (credit_acc, trans); + dclearedBalance -= credit_acc -> running_cleared_balance; + dclearedBalance += tmp; + credit_acc -> running_cleared_balance = tmp; + } + } + + if (is_debit) { + tmp = xaccGetBalance (debit_acc, trans); + dbalance -= debit_acc -> running_balance; + dbalance += tmp; + debit_acc -> running_balance = tmp; + + if( NREC != trans->reconciled ) { + tmp = xaccGetClearedBalance (debit_acc, trans); + dclearedBalance -= debit_acc -> running_cleared_balance; + dclearedBalance += tmp; + debit_acc -> running_cleared_balance = tmp; + } + } + /* for income and expense acounts, we have to reverse - * the meaning of balance, since, in a cual entry + * the meaning of balance, since, in a dual entry * system, income will show up as a credit to a * bank account, and a debit to the income account. * Thus, positive and negative are interchanged */ - if( (EXPENSE == acc->type) || - (INCOME == acc->type) ) { - dbalance = - dbalance; + prt_balance = dbalance; + if( (EXPENSE == regData->type) || + (INCOME == regData->type) || + (INC_LEDGER == regData->type) ) { + prt_balance = - dbalance; + prt_clearedBalance = - dclearedBalance; } - if( reg != NULL ) - { + if( reg != NULL ) { #ifdef USE_NO_COLOR - sprintf( buf, "%.2f ", dbalance ); + sprintf( buf, "%.2f ", prt_balance ); #else - sprintf( buf, "%.2f ", DABS(dbalance) ); + sprintf( buf, "%.2f ", DABS(prt_balance) ); /* Set the color of the text, depending on whether the * balance is negative or positive */ - if( 0.0 > dbalance ) - XbaeMatrixSetCellColor( reg, position, BALN_CELL_C, negPixel ); - else - XbaeMatrixSetCellColor( reg, position, BALN_CELL_C, posPixel ); + if( 0.0 > prt_balance ) { + XbaeMatrixSetCellColor( reg, position+BALN_CELL_R, + BALN_CELL_C, negPixel ); + } else { + XbaeMatrixSetCellColor( reg, position+BALN_CELL_R, + BALN_CELL_C, posPixel ); + } #endif /* Put the value in the cell */ - XbaeMatrixSetCell( reg, position, BALN_CELL_C, buf ); + XbaeMatrixSetCell( reg, position+BALN_CELL_R, BALN_CELL_C, buf ); /* update share balances too ... */ - if( (MUTUAL == acc->type) || - (PORTFOLIO == acc->type) ) - { + switch (regData->type) { + case MUTUAL: + case STOCK: { + double value = 0.0; + double share_balance = 0.0; + Account *acc; + + /* mutual and stock types have just one account */ + acc = regData->blackacc[0]; + /* ------------------------------------ */ + /* do the value of the transaction */ + value = xaccGetAmount (acc, trans); + + /* if the value is zero, just leave the cell blank */ + if (DEQ (0.0, value)) { + buf[0] = 0x0; + } else { #ifdef USE_NO_COLOR - sprintf( buf, "%.3f ", share_balance ); + sprintf( buf, "%.2f ", value ); #else - sprintf( buf, "%.3f ", DABS(share_balance) ); + sprintf( buf, "%.2f ", DABS(value) ); +#endif + } - /* Set the color of the text, depending on whether the - * balance is negative or positive */ - if( 0.0 > share_balance ) - XbaeMatrixSetCellColor( reg, position, SHRS_CELL_C, negPixel ); - else - XbaeMatrixSetCellColor( reg, position, SHRS_CELL_C, posPixel ); +#ifndef USE_NO_COLOR + /* Set the color of the text, depending on whether the + * balance is negative or positive */ + if( 0.0 > value ) { + XbaeMatrixSetCellColor( reg, position+VCRD_CELL_R, + VCRD_CELL_C, negPixel ); + } else { + XbaeMatrixSetCellColor( reg, position+VCRD_CELL_R, + VCRD_CELL_C, posPixel ); + } #endif - - /* Put the value in the cell */ - XbaeMatrixSetCell( reg, position, SHRS_CELL_C, buf ); + /* Put the value in the cell */ + XbaeMatrixSetCell( reg, position+VCRD_CELL_R, VCRD_CELL_C, buf ); + + /* ------------------------------------ */ + /* now show the share balance */ + share_balance = xaccGetShareBalance (acc, trans); +#ifdef USE_NO_COLOR + sprintf( buf, "%.3f ", share_balance ); +#else + sprintf( buf, "%.3f ", DABS(share_balance) ); + + /* Set the color of the text, depending on whether the + * balance is negative or positive */ + if( 0.0 > share_balance ) { + XbaeMatrixSetCellColor( reg, position+SHRS_CELL_R, + SHRS_CELL_C, negPixel ); + } else { + XbaeMatrixSetCellColor( reg, position+SHRS_CELL_R, + SHRS_CELL_C, posPixel ); + } +#endif + + /* Put the share balance in the cell */ + XbaeMatrixSetCell( reg, position+SHRS_CELL_R, SHRS_CELL_C, buf ); + } + break; + + case PORTFOLIO: { + double value = 0.0; + double share_balance = 0.0; + int show = 0; + Account *acc; + + /* ------------------------------------ */ + /* show the value of the transaction, but only if the + * account belongs on this ledger */ + value = trans->damount * trans->share_price; + +#ifndef USE_NO_COLOR + /* Set the color of the text, depending on whether the + * value is negative or positive. Remebr that we flip + * the color value for debit cells */ + if( 0.0 > value ) { + XbaeMatrixSetCellColor( reg, position+VDEB_CELL_R, + VDEB_CELL_C, posPixel ); + XbaeMatrixSetCellColor( reg, position+VCRD_CELL_R, + VCRD_CELL_C, negPixel ); + } else { + XbaeMatrixSetCellColor( reg, position+VDEB_CELL_R, + VDEB_CELL_C, negPixel ); + XbaeMatrixSetCellColor( reg, position+VCRD_CELL_R, + VCRD_CELL_C, posPixel ); + } +#endif + value = trans->damount * trans->share_price; +#ifdef USE_NO_COLOR + value = -value; /* flip sign for debit accounts */ +#else + value = DABS(value); +#endif + acc = (Account *) (trans->debit); + show = xaccIsAccountInList (acc, regData->blackacc); + /* show only if value is non-zero (it may be zero if a price + * is recorded) */ + if (show && !(DEQ(0.0, value))) { + sprintf( buf, "%.2f ", value ); + } else { + buf[0] = 0x0; + } + XbaeMatrixSetCell( reg, position+VDEB_CELL_R, VDEB_CELL_C, buf ); - position+=2; /* each transaction has two rows */ + value = trans->damount * trans->share_price; +#ifdef USE_NO_COLOR + value = +value; /* DO NOT flip sign for credit accounts */ +#else + value = DABS(value); +#endif + acc = (Account *) (trans->credit); + show = xaccIsAccountInList (acc, regData->blackacc); + /* show only if value is non-zero (it may be zero if a price + * is recorded) */ + if (show && !(DEQ(0.0, value))) { + sprintf( buf, "%.2f ", value ); + } else { + buf[0] = 0x0; + } + XbaeMatrixSetCell( reg, position+VCRD_CELL_R, VCRD_CELL_C, buf ); + + /* ------------------------------------ */ + /* show the share balance */ + /* show the share balance *only* either the debit or credit + * account belongs in this ledger, *and* if the transaction + * type is mutual or stock. */ + show = 0; + share_balance = 0.0; + + acc = (Account *) (trans->debit); + if (acc) { + if ((MUTUAL == acc->type) || (STOCK == acc->type)) { + show += xaccIsAccountInList (acc, regData->blackacc); + share_balance = xaccGetShareBalance (debit_acc, trans); + } + } + + acc = (Account *) (trans->credit); + if (acc) { + if ((MUTUAL == acc->type) || (STOCK == acc->type)) { + show += xaccIsAccountInList (acc, regData->blackacc); + share_balance = xaccGetShareBalance (credit_acc, trans); + } + } + + if (show) { +#ifdef USE_NO_COLOR + sprintf( buf, "%.3f ", share_balance ); +#else + sprintf( buf, "%.3f ", DABS(share_balance) ); + + /* Set the color of the text, depending on whether the + * share balance is negative or positive */ + if( 0.0 > share_balance ) { + XbaeMatrixSetCellColor( reg, position+SHRS_CELL_R, + SHRS_CELL_C, negPixel ); + } else { + XbaeMatrixSetCellColor( reg, position+SHRS_CELL_R, + SHRS_CELL_C, posPixel ); + } +#endif + + /* Put the share balance in the cell */ + XbaeMatrixSetCell( reg, position+SHRS_CELL_R, SHRS_CELL_C, buf ); + } else { + XbaeMatrixSetCell( reg, position+SHRS_CELL_R, SHRS_CELL_C, "" ); + } + + } + break; + + default: + break; } } - - if( NULL != regData->balance ) - { - sprintf( buf, "$ %.2f \n$ %.2f \0", - dbalance, dclearedBalance ); - + } + if( NULL != regData->balance ) { + sprintf( buf, "$ %.2f\n$ %.2f", + prt_balance, prt_clearedBalance ); XmTextSetString( regData->balance, buf ); - } + } - refreshMainWindow(); /* make sure the balance field in - * the main window is up to date */ + /* make sure the balance field in the main + * window is up to date. */ + refreshMainWindow(); + return dbalance; } @@ -500,55 +1003,51 @@ regRecalculateBalance( RegWindow *regData ) \********************************************************************/ /* RECALC_BALANCE recomputes the balance shown in - * the register window, if its is visible + * register/ledger windows, if they are visible. */ #define RECALC_BALANCE(sacc) { \ Account * xfer_acc; \ RegWindow * xfer_reg; \ + struct _RegWindow **list; \ xfer_acc = (Account *) (sacc); \ if (xfer_acc) { \ - xfer_reg = (RegWindow *) (xfer_acc->regData); \ - if (xfer_reg) { \ - regRecalculateBalance (xfer_reg); \ + list = xfer_acc->ledgerList; \ + if (list) { \ + int n = 0; \ + xfer_reg = (RegWindow *) (list [0]); \ + while (xfer_reg) { \ + regRecalculateBalance (xfer_reg); \ + n++; \ + xfer_reg = (RegWindow *) (list [n]); \ + } \ } \ } \ } -/* REFRESH_REGISTER redisplays the register window, - * if it is visible +/* REFRESH_REGISTER redisplays the register/ledger windows, + * if they are visible. */ #define REFRESH_REGISTER(sacc) { \ Account * xfer_acc; \ RegWindow * xfer_reg; \ + struct _RegWindow **list; \ xfer_acc = (Account *) (sacc); \ if (xfer_acc) { \ - xfer_reg = (RegWindow *) (xfer_acc->regData); \ - if (xfer_reg) { \ - regRefresh (xfer_reg); \ + list = xfer_acc->ledgerList; \ + if (list) { \ + int n = 0; \ + xfer_reg = (RegWindow *) (list [0]); \ + while (xfer_reg) { \ + regRefresh (xfer_reg); \ + n++; \ + xfer_reg = (RegWindow *) (list [n]); \ + } \ } \ } \ } -/* DATE_REORDER needs to null out lastTrans, because - * during date reordering we removed and re-inserted - * the transaction... reset lastTrans to zero to prevent it from - * indicating a row that doesn't exist. (That shouldn't happen - * anyways!) - */ -#define DATE_REORDER(sacc) { \ - Account * xfer_acc; \ - RegWindow * xfer_reg; \ - xfer_acc = (Account *) (sacc); \ - if (xfer_acc) { \ - xfer_reg = (RegWindow *) (xfer_acc->regData); \ - if (xfer_reg) { \ - xfer_reg->lastTrans = 0; \ - } \ - } \ -} - /* REMOVE_TRANS will not only remove a transaction from an account, * but it will also delete the appropriate rows from the register * window, if the register window is visible @@ -564,14 +1063,15 @@ regRecalculateBalance( RegWindow *regData ) \ /* remove the rows from the matrix */ \ if (otherRegData) { \ - int otherrow = 2*n + 1; \ - XbaeMatrixDeleteRows( otherRegData->reg, otherrow, 2 ); \ + int otherrow = NUM_ROWS_PER_TRANS*n + NUM_HEADER_ROWS; \ + XbaeMatrixDeleteRows( otherRegData->reg, \ + otherrow, NUM_ROWS_PER_TRANS ); \ XbaeMatrixRefresh( otherRegData->reg); \ } \ } \ } -void +static void regSaveTransaction( RegWindow *regData, int position ) { /* save transaction structure... in order to speed this up, @@ -579,17 +1079,14 @@ regSaveTransaction( RegWindow *regData, int position ) * that might have changed, so we only have to save the stuff * that has changed */ char buf[BUFSIZE]; - int row = (position * 2) + 1; - Account *acc = regData->acc; + int row = position * NUM_ROWS_PER_TRANS + NUM_HEADER_ROWS; Transaction *trans; + Boolean dateOutOfOrder = False; /* If nothing has changed, we have nothing to do */ if (MOD_NONE == regData->changed) return; - /* Be sure to prompt the user to save to disk after changes are made! */ - acc->parent->saved = False; - - trans = getTransaction( acc, position ); + trans = (Transaction *) XbaeMatrixGetRowUserData (regData->reg, row); if( trans == NULL ) { @@ -597,30 +1094,58 @@ regSaveTransaction( RegWindow *regData, int position ) DEBUG("New Transaction\n"); trans = mallocTransaction(); - trans->credit = (struct _account *) acc; - - /* insert the transaction now. If we later discover that - * the user hs not made any entries, we will remove it again. - * However, for some of the itntermediate processing, we must - * have a valid transaction present in the account. - */ - insertTransaction (acc, trans); regData->changed = MOD_ALL; + + if (1 == regData->numAcc) { + /* Be sure to prompt the user to save to disk after changes are made! */ + Account *acc = regData->blackacc[0]; + acc->parent->saved = False; + } + } else { + /* Be sure to prompt the user to save to disk after changes are made! */ + Account *acc; + acc = (Account *) trans->credit; + if (acc) acc->parent->saved = False; + acc = (Account *) trans->debit; + if (acc) acc->parent->saved = False; } + if( regData->changed & MOD_NUM ) { DEBUG("MOD_NUM\n"); /* ...the transaction number (String)... */ XtFree( trans->num ); - trans->num = XtNewString( XbaeMatrixGetCell(regData->reg,row,NUM_CELL_C) ); + trans->num = XtNewString( + XbaeMatrixGetCell(regData->reg,row+NUM_CELL_R,NUM_CELL_C)); } - if( regData->changed & MOD_XFER ) + if( regData->changed & MOD_XFRM ) { /* ... the transfer ... */ char * name; - Account *xfer_acct = xaccGetOtherAccount (acc, trans); - DEBUG("MOD_XFER\n"); + Account *xfer_acct = NULL; + + DEBUG("MOD_XFRM\n"); + + /* the way that transfers are handled depends on whether this + * is a ledger account, or a single-account register */ + if ( (GEN_LEDGER == regData->type) || + (INC_LEDGER == regData->type) || + (PORTFOLIO == regData->type) ) { + + /* for a general ledger, the transfer-from account is obvious */ + xfer_acct = (Account *) trans->debit; + } else { + + /* if not a ledger, then there is only one account, + * and the transfer account is the other half of the + * pairing. -- Unless, of course, this is a new transaction. */ + if( regData->changed & MOD_NEW) { + xfer_acct = NULL; + } else { + xfer_acct = xaccGetOtherAccount (regData->blackacc[0], trans); + } + } if (xfer_acct) { /* remove the transaction from wherever it used to be */ @@ -630,14 +1155,77 @@ regSaveTransaction( RegWindow *regData, int position ) RECALC_BALANCE (xfer_acct); REFRESH_REGISTER (xfer_acct); } - + /* get the new account name */ - name = XbaeMatrixGetCell(regData->reg,row+XFER_CELL_R, XFER_CELL_C); + name = XbaeMatrixGetCell(regData->reg,row+XFRM_CELL_R, XFRM_CELL_C); /* get the new account from the name */ - xfer_acct = xaccGetPeerAccountFromName (acc, name); + xfer_acct = xaccGetAccountFromName (topgroup, name); if (xfer_acct) { + + /* for a new transaction, the default will be that the + * transfer occurs from the debited account */ + if( regData->changed & MOD_NEW) { + trans->debit = (struct _account *)xfer_acct; + } + + /* for a general ledger, the transfer *must* occur + * from the debited account. */ + if ( (GEN_LEDGER == regData->type) || + (INC_LEDGER == regData->type) || + (PORTFOLIO == regData->type) ) { + trans->debit = (struct _account *)xfer_acct; + } + + /* for non-new transactions, the transfer may be from the + * debited or the credited account. Which one it was depends + * entirely on which account pointer is null after the + * removal of the old entry. The insertTransaction() + * subroutine will find the null slot, and will insert + * into it automatically. */ + + /* insert the transaction into the new account */ + insertTransaction (xfer_acct, trans); + } + } + + + /* if not a ledger, then we shouldn't get here ... */ + if( (regData->changed & MOD_XTO ) && + ((GEN_LEDGER == regData->type) || + (INC_LEDGER == regData->type) || + (PORTFOLIO == regData->type)) ) + { + /* ... the transfer ... */ + char * name; + Account *xfer_acct; + + DEBUG("MOD_XTO\n"); + + /* for a general ledger, from and to are easy to determine */ + xfer_acct = (Account *) trans->credit; + + if (xfer_acct) { + /* remove the transaction from wherever it used to be */ + REMOVE_TRANS (xfer_acct, trans); + + /* recalculate the balance and redisplay the window for the old acct */ + RECALC_BALANCE (xfer_acct); + REFRESH_REGISTER (xfer_acct); + } + + /* get the new account name */ + name = XbaeMatrixGetCell(regData->reg,row+XTO_CELL_R, XTO_CELL_C); + + /* get the new account from the name */ + xfer_acct = xaccGetAccountFromName (topgroup, name); + + if (xfer_acct) { + /* for a ledger, the transfer-to account is always the credited + * account. */ + trans->credit = (struct _account *)xfer_acct; + /* insert the transaction into the new account */ insertTransaction (xfer_acct, trans); } @@ -649,7 +1237,8 @@ regSaveTransaction( RegWindow *regData, int position ) /* ... the description... */ XtFree( trans->description ); trans->description = - XtNewString( XbaeMatrixGetCell(regData->reg,row,DESC_CELL_C) ); + XtNewString( + XbaeMatrixGetCell(regData->reg,row+DESC_CELL_R,DESC_CELL_C) ); } if( regData->changed & MOD_MEMO ) @@ -663,15 +1252,13 @@ regSaveTransaction( RegWindow *regData, int position ) trans->memo = XtNewString( tmp ); } - /* ignore MOD_ACTN for non-stock accounts */ - if( (regData->changed & MOD_ACTN) && - ((MUTUAL == acc->type) || (PORTFOLIO==acc->type)) ) + if( regData->changed & MOD_ACTN ) { String actn = NULL; DEBUG("MOD_ACTN\n"); /* ... the action ... */ XtFree( trans->action ); - actn = XbaeMatrixGetCell(regData->reg,row,ACTN_CELL_C); + actn = XbaeMatrixGetCell(regData->reg,row+ACTN_CELL_R,ACTN_CELL_C); trans->action = XtNewString( actn ); } @@ -679,47 +1266,163 @@ regSaveTransaction( RegWindow *regData, int position ) { DEBUG("MOD_RECN\n"); /* ...the reconciled flag (char)... */ - trans->reconciled = (XbaeMatrixGetCell(regData->reg,row,RECN_CELL_C))[0]; + trans->reconciled = + (XbaeMatrixGetCell(regData->reg,row+RECN_CELL_R,RECN_CELL_C))[0]; } if( regData->changed & MOD_AMNT ) { String amount; float val=0.0; /* must be float for sscanf to work */ - double themount = 0.0; + double debit_amount = 0.0; + double credit_amount = 0.0; DEBUG("MOD_AMNT\n"); - /* ...and the amounts */ - amount = XbaeMatrixGetCell(regData->reg,row,DEP_CELL_C); + + val = 0.0; + amount = XbaeMatrixGetCell(regData->reg,row+PAY_CELL_R,PAY_CELL_C); sscanf( amount, "%f", &val ); - themount = val; - + debit_amount = val; + val = 0.0; - amount = XbaeMatrixGetCell(regData->reg,row,PAY_CELL_C); + amount = XbaeMatrixGetCell(regData->reg,row+DEP_CELL_R,DEP_CELL_C); sscanf( amount, "%f", &val ); - themount -= val; + credit_amount = val; - xaccSetShareAmount (acc, trans, themount); + switch (regData->type) { + case BANK: + case CASH: + case ASSET: + case CREDIT: + case LIABILITY: + case INCOME: + case EXPENSE: + case EQUITY: { + double themount = credit_amount - debit_amount; + Account *main_acc = regData->blackacc[0]; + xaccSetShareAmount (main_acc, trans, themount); - /* Reset so there is only one field filled */ - if( 0.0 > themount ) - { - /* hack alert -- should keep 3 digits for share amounts */ - sprintf( buf, "%.2f ", -themount ); - XbaeMatrixSetCell( regData->reg, row, PAY_CELL_C, buf ); - XbaeMatrixSetCell( regData->reg, row, DEP_CELL_C, "" ); - } - else - { - sprintf( buf, "%.2f ", themount ); - XbaeMatrixSetCell( regData->reg, row, PAY_CELL_C, "" ); - XbaeMatrixSetCell( regData->reg, row, DEP_CELL_C, buf ); + /* Reset so there is only one field filled */ + if( 0.0 > themount ) + { + sprintf( buf, "%.2f ", -themount ); + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, buf ); + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, "" ); + } + else + { + sprintf( buf, "%.2f ", themount ); + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, "" ); + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, buf ); + } + } + break; + + case STOCK: + case MUTUAL: { + double themount = credit_amount - debit_amount; + Account *main_acc = regData->blackacc[0]; + xaccSetShareAmount (main_acc, trans, themount); + + /* Reset so there is only one field filled */ + /* if share amount is zero (e.g. for a price quote) + * then leave both cells blank */ + if (DEQ (0.0, themount) ) + { + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, "" ); + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, "" ); + } else + if( 0.0 > themount ) + { + sprintf( buf, "%.3f ", -themount ); + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, buf ); + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, "" ); + } + else + { + sprintf( buf, "%.3f ", themount ); + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, "" ); + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, buf ); + } + } + break; + + case PORTFOLIO: + case INC_LEDGER: + case GEN_LEDGER: { + Account *acc; + double themount = 0.0; + int show_debit, show_credit; + + acc = (Account *) (trans->debit); + show_debit = xaccIsAccountInList (acc, regData->blackacc); + acc = (Account *) (trans->credit); + show_credit = xaccIsAccountInList (acc, regData->blackacc); + + if (show_debit && show_credit) { + /* try to figure out which entry the user changed ! */ + if (DEQ (debit_amount, trans->damount)) themount = credit_amount; + if (DEQ (credit_amount, trans->damount)) themount = debit_amount; + } else { + themount = credit_amount - debit_amount; + } + trans->damount = themount; + + /* attempt to keep all displayed quantities positive by + * flipping signs and columns for negative entries */ + /* hack alert -- but does this really work ??? */ + if (0.0 < themount) { + if (PORTFOLIO == regData->type) { + sprintf( buf, "%.3f ", themount ); + } else { + sprintf( buf, "%.2f ", themount ); + } + + if (show_debit) { + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, buf ); + } else { + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, "" ); + } + + if (show_credit) { + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, buf ); + } else { + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, "" ); + } + } else { + themount = - themount; + + if (PORTFOLIO == regData->type) { + sprintf( buf, "%.3f ", themount ); + } else { + sprintf( buf, "%.2f ", themount ); + } + + if (show_debit) { + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, buf ); + } else { + XbaeMatrixSetCell( regData->reg, row+DEP_CELL_R, DEP_CELL_C, "" ); + } + + if (show_credit) { + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, buf ); + } else { + XbaeMatrixSetCell( regData->reg, row+PAY_CELL_R, PAY_CELL_C, "" ); + } + } } + break; + + default: + break; } + } /* ignore MOD_PRIC for non-stock accounts */ if( (regData->changed & MOD_PRIC) && - ((MUTUAL == acc->type) || (PORTFOLIO==acc->type)) ) + ((MUTUAL == regData->type) || + (STOCK == regData->type) || + (PORTFOLIO == regData->type)) ) { String price; float val=0.0; /* must be float for sscanf to work */ @@ -727,17 +1430,65 @@ regSaveTransaction( RegWindow *regData, int position ) DEBUG("MOD_PRIC\n"); /* ...the price flag ... */ - price = XbaeMatrixGetCell(regData->reg,row,PRIC_CELL_C); + price = XbaeMatrixGetCell(regData->reg,row+PRCC_CELL_R,PRCC_CELL_C); sscanf( price, "%f", &val ); trans->share_price = val; sprintf( buf, "%.2f ", trans->share_price ); - XbaeMatrixSetCell( regData->reg, row, PRIC_CELL_C, buf ); + XbaeMatrixSetCell( regData->reg, row+PRCC_CELL_R, PRCC_CELL_C, buf ); } + /* If this is a new transaction, and the user did not + * actually enter any data, then we should not really + * consider this to be a new transaction! Free it, and + * bail out */ + if (regData->changed & MOD_NEW) { + if( (strcmp("",trans->num) == 0) && + (strcmp("",trans->description) == 0) && + (strcmp("",trans->memo) == 0) && + /* (strcmp("",trans->action) == 0) && annoying! */ + /* (0 == trans->catagory) && not implemented ! */ + /* (NULL == trans->credit) && annoying! */ + /* (NULL == trans->debit) && annoying! */ + (1.0 == trans->share_price) && + (0.0 == trans->damount) ) { + freeTransaction (trans); + return; + } + + /* if we got to here, we've got a live one. Insert it into + * an account, if we haven't done so already. Do this + * before we get to the date code below, since date the + * date code can/will break if the transaction is not yet + * inserted. */ + + if (1 == regData->numAcc) { + Account * acc = regData->blackacc[0]; + + /* for single-account registers, the insertion account + * is always the credited account */ + trans->credit = (struct _account *) acc; + insertTransaction (acc, trans); + } + } + + /* for ledgers, the user *MUST* specify either a + * credited or a debited account, or both. If they + * have specified these, then the account is already + * inserted, and we have nothing to do. If they have + * not specified either one, then it is an error + * condition -- we cannot insert, because we don't know + * where to insert. + * + * Warn the user about this. */ + if ((NULL == trans->credit) && (NULL == trans->debit)) { + errorBox (toplevel, XFER_NO_ACC_MSG); + freeTransaction (trans); + return; + } + if( regData->changed & MOD_DATE ) { - Boolean outOfOrder = False; DEBUG("MOD_DATE\n"); /* read in the date stuff... */ @@ -745,57 +1496,69 @@ regSaveTransaction( RegWindow *regData, int position ) &(trans->date.month), &(trans->date.day) ); - trans->date.year = atoi(XbaeMatrixGetCell(regData->reg,row+YEAR_CELL_R,YEAR_CELL_C)); + trans->date.year = atoi(XbaeMatrixGetCell(regData->reg, + row+YEAR_CELL_R,YEAR_CELL_C)); /* take care of re-ordering implications on the register. * If the date changed on a double-entry (transfer) transaction, * then make sure that both register windows are updated .. */ - outOfOrder = xaccCheckDateOrderDE (trans); - if( outOfOrder ) - { - int pos; - - DATE_REORDER ((trans->credit)); - DATE_REORDER ((trans->debit)); - - /* Scroll to the new location of the reordered transaction; - * but do this only for this register, not any other register - * windows. */ - pos = getNumOfTransaction (acc, trans); - XbaeMatrixMakeCellVisible( regData->reg, 2*pos+1, DESC_CELL_C ); - } + dateOutOfOrder = xaccCheckDateOrderDE (trans); } - /* - * If this is a new transaction, and the user did not - * actually enter any data, then we should not really - * consider this to be a new transaction! */ - if (regData->changed & MOD_NEW) { - if( (strcmp("",trans->num) == 0) && - (strcmp("",trans->description) == 0) && - (strcmp("",trans->memo) == 0) && - (strcmp("",trans->action) == 0) && - (0 == trans->catagory) && - (NULL == xaccGetOtherAccount (acc, trans)) && - (NULL == trans->debit) && - (1.0 == trans->share_price) && - (0.0 == trans->damount) ) - { - xaccRemoveTransaction (acc, trans); - freeTransaction (trans); - return; - } - } - /* For many, but not all changes, we need to * recalculate the balances */ - if( regData->changed & (MOD_XFER | MOD_RECN | MOD_AMNT | MOD_PRIC | MOD_NEW)) { + if( regData->changed & (MOD_XFRM | MOD_XTO | MOD_RECN | + MOD_AMNT | MOD_PRIC | MOD_NEW)) { RECALC_BALANCE ((trans->credit)); RECALC_BALANCE ((trans->debit)); + + /* if this is a ledger window, then we have to update + * it as well. If this is not a ledger window, one of + * the above two lines will have handled it already */ + if (1 < regData->numAcc) { + regRecalculateBalance (regData); + } } REFRESH_REGISTER ((trans->credit)); REFRESH_REGISTER ((trans->debit)); + /* if this is a ledger window, then we have to update + * it as well. If this is not a ledger window, one of + * the above two lines will have handled it already */ + if (1 < regData->numAcc) { + regRefresh (regData); + } + + /* if the update changed the date ordering of the transactions, + * then scroll the register to the new location of the transaction. + * Do so only for this window, not any of the others. */ + if( dateOutOfOrder ) { + int newrow; + Transaction *nt; + + /* find the location of the new row ... */ + newrow = NUM_HEADER_ROWS; + nt = (Transaction *) XbaeMatrixGetRowUserData (regData->reg, newrow); + while (nt) { + if (trans == nt) break; + newrow += NUM_ROWS_PER_TRANS; + nt = (Transaction *) XbaeMatrixGetRowUserData (regData->reg, newrow); + } + XbaeMatrixMakeCellVisible( regData->reg, newrow+DATE_CELL_R, DATE_CELL_C ); + + /* Set the currEntry transaction pointer for this register window. + * During date reordering we removed and re-inserted + * the transaction at a new location. currEntry should reflect that + * new location, in casee the user does additional editing. + */ + regData->currEntry = (newrow-NUM_HEADER_ROWS)/NUM_ROWS_PER_TRANS; + } + + /* unmap the pop boxes, otherwise they get confused about + * which row/transaction they belong to */ + SetPopBox (regData->actbox, -1, -1); + SetPopBox (regData->xfrmbox, -1, -1); + SetPopBox (regData->xtobox, -1, -1); /* reset the "changed" bitfield */ regData->changed = 0; @@ -818,7 +1581,7 @@ regSaveTransaction( RegWindow *regData, int position ) } /********************************************************************\ - * regWindow * + * regWindowSimple * * opens up a register window for Account account * * * * Args: parent - the parent of this window * @@ -826,13 +1589,113 @@ regSaveTransaction( RegWindow *regData, int position ) * Return: regData - the register window instance * \********************************************************************/ RegWindow * -regWindow( Widget parent, Account *acc ) +regWindowSimple( Widget parent, Account *acc ) + { + RegWindow *retval; + Account *acclist[2]; + + acclist[0] = acc; + acclist[1] = NULL; + + /* don't allow more than one regster window for this account */ + /* hack alert -- we should raise this window to the top, if + * we are called, and the register already exists */ + if (acc->regData) return acc->regData; + + retval = regWindowLedger (parent, acclist, acc->type); + return retval; + } + +/********************************************************************\ + * regWindowAccGroup * + * opens up a register window for a group of Accounts * + * * + * Args: parent - the parent of this window * + * acc - the account associated with this register * + * Return: regData - the register window instance * +\********************************************************************/ +RegWindow * +regWindowAccGroup( Widget parent, Account *acc ) + { + RegWindow *retval; + Account **list; + int ledger_type; + Account *le; + int n; + + /* don't allow more than one ledger window for this account */ + /* hack alert -- we should raise this window to the top, if + * we are called, and the ledger already exists */ + if (acc->regLedger) return acc->regLedger; + + list = xaccGroupToList (acc); + + switch (acc->type) { + case BANK: + case CASH: + case ASSET: + case CREDIT: + case LIABILITY: + /* if any of the sub-accounts have STOCK or MUTUAL types, + * then we must use the PORTFOLIO type ledger. Otherise, + * a plain old GEN_LEDGER will do. */ + ledger_type = GEN_LEDGER; + + le = list[0]; + n = 0; + while (le) { + if ((STOCK == le->type) || (MUTUAL == le->type)) { + ledger_type = PORTFOLIO; + } + n++; + le = list[n]; + } + break; + + case STOCK: + case MUTUAL: + ledger_type = PORTFOLIO; + break; + + case INCOME: + case EXPENSE: + ledger_type = INC_LEDGER; + break; + + case EQUITY: + ledger_type = GEN_LEDGER; + break; + + default: + PERR (" regWindowAccGroup(): unknown account type \n"); + _free (list); + return; + } + retval = regWindowLedger (parent, list, ledger_type); + acc->regLedger = retval; + + if (list) _free (list); + + return retval; + } + +/********************************************************************\ + * regWindowLedger * + * opens up a ledger window for the account list * + * * + * Args: parent - the parent of this window * + * acc - the account associated with this register * + * Return: regData - the register window instance * +\********************************************************************/ +RegWindow * +regWindowLedger( Widget parent, Account **acclist, int ledger_type ) { - AccountGroup *grp; Transaction *trans; RegWindow *regData; Widget menubar, pane, buttonform, frame, reg, widget; int position=0; + char *windowname; + char buf [BUFSIZE]; setBusyCursor( parent ); @@ -849,19 +1712,49 @@ regWindow( Widget parent, Account *acc ) } regData = (RegWindow *)_malloc(sizeof(RegWindow)); - acc -> regData = regData; /* avoid having two open registers for one account */ - regData->acc = acc; regData->changed = 0; /* Nothing has changed yet! */ - regData->lastTrans = 0; - regData->qf = acc->qfRoot; + regData->currEntry = 0; regData->insert = 0; /* the insert (cursor) position in * quickfill cells */ - + + /* count the number of accounts we are supposed to display, + * and then, store them. */ + regData->numAcc = accListCount (acclist); + regData->blackacc = accListCopy (acclist); + + if (0 == regData->numAcc) { + /* this is pretty much an error condition. bail out. */ + unsetBusyCursor( parent ); + _free (regData); + return NULL; + } + + regData->type = ledger_type; + + if (1 == regData->numAcc) { + regData->qf = regData->blackacc[0]->qfRoot; + + /* avoid having two open registers for one account */ + regData->blackacc[0]->regData = regData; + windowname = regData->blackacc[0]->accountName; + } else { + sprintf (buf, "%s General Ledger", regData->blackacc[0]->accountName); + windowname = buf; + + /* hack alert -- quickfill for ledgers is almost certainly broken */ + regData->qf = regData->blackacc[0]->qfRoot; + + /* associate register with account, so that we can do consistent + * updates */ + regData->blackacc[0]->regLedger = regData; + } + ledgerListAddList (regData->blackacc, regData); + regData->dialog = XtVaCreatePopupShell( "dialog", xmDialogShellWidgetClass, parent, XmNdeleteResponse, XmDESTROY, - XmNtitle, acc->accountName, + XmNtitle, windowname, /* XmNwidth, 395, XmNheight, 400, @@ -899,43 +1792,43 @@ regWindow( Widget parent, Account *acc ) \******************************************************************/ { MenuItem reportMenu[] = { - { "Simple...", &xmPushButtonWidgetClass, 'S', NULL, NULL, + { "Simple...", &xmPushButtonWidgetClass, 'S', NULL, NULL, True, NULL, (XtPointer)0, (MenuItem *)NULL }, NULL, }; MenuItem activityMenu[] = { - { "Transfer...", &xmPushButtonWidgetClass, 'T', NULL, NULL, + { "Transfer...", &xmPushButtonWidgetClass, 'T', NULL, NULL, True, accountMenubarCB, (XtPointer)AMB_TRNS, (MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "Reconcile...", &xmPushButtonWidgetClass, 'C', NULL, NULL, + { "Reconcile...", &xmPushButtonWidgetClass, 'C', NULL, NULL, True, startRecnCB, NULL, (MenuItem *)NULL }, - { "Adjust Balance...", &xmPushButtonWidgetClass, 'A', NULL, NULL, + { "Adjust Balance...", &xmPushButtonWidgetClass, 'A', NULL, NULL, True, startAdjBCB, NULL, (MenuItem *)NULL }, - { "Report", &xmPushButtonWidgetClass, 'D', NULL, NULL, + { "Report", &xmPushButtonWidgetClass, 'D', NULL, NULL, True, NULL, (XtPointer)0, (MenuItem *)&reportMenu }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "Delete Transaction", &xmPushButtonWidgetClass, 'D', NULL, NULL, + { "Delete Transaction", &xmPushButtonWidgetClass, 'D', NULL, NULL, True, deleteCB, NULL, (MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "Close Window", &xmPushButtonWidgetClass, 'Q', NULL, NULL, + { "Close Window", &xmPushButtonWidgetClass, 'Q', NULL, NULL, True, destroyShellCB, NULL, (MenuItem *)NULL }, NULL, }; MenuItem helpMenu[] = { - { "About...", &xmPushButtonWidgetClass, 'A', NULL, NULL, + { "About...", &xmPushButtonWidgetClass, 'A', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_ABOUT, (MenuItem *)NULL }, - { "Help...", &xmPushButtonWidgetClass, 'H', NULL, NULL, + { "Help...", &xmPushButtonWidgetClass, 'H', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_REGWIN,(MenuItem *)NULL }, - { "", &xmSeparatorWidgetClass, 0, NULL, NULL, + { "", &xmSeparatorWidgetClass, 0, NULL, NULL, True, NULL, NULL, (MenuItem *)NULL }, - { "License...", &xmPushButtonWidgetClass, 'L', NULL, NULL, + { "License...", &xmPushButtonWidgetClass, 'L', NULL, NULL, True, helpMenubarCB, (XtPointer)HMB_LIC, (MenuItem *)NULL }, NULL, }; @@ -947,6 +1840,12 @@ regWindow( Widget parent, Account *acc ) activityMenu[6].callback_data=(XtPointer)regData; activityMenu[8].callback_data=(XtPointer)(regData->dialog); /* destroy callback */ + /* can't adjust the balance on a ledger window */ + if (1 != regData->numAcc) { + activityMenu[2].sensitive = False; + activityMenu[3].sensitive = False; + } + menubar = XmCreateMenuBar( pane, "menubar", NULL, 0 ); BuildMenu( menubar, XmMENU_PULLDOWN, "Activities", 'A', @@ -970,9 +1869,23 @@ regWindow( Widget parent, Account *acc ) String **data; int i,j; /* ----------------------------------- */ - /* define where each column shows up, and total number of columns. */ - /* the number on the right hand side is the physical location of the column */ - switch(acc->type) + /* define where each column shows up, and total number + * of columns for this register type. The number on the + * right hand side is the physical location of the column */ + + regData->cellColLocation [DATE_CELL_ID] = 0; + regData->cellColLocation [YEAR_CELL_ID] = 0; + regData->cellColLocation [NUM_CELL_ID] = 1; + regData->cellColLocation [ACTN_CELL_ID] = 1; + regData->cellColLocation [XFRM_CELL_ID] = 2; + regData->cellColLocation [XTO_CELL_ID] = 2; + regData->cellColLocation [DESC_CELL_ID] = 3; + regData->cellColLocation [MEMO_CELL_ID] = 3; + regData->cellColLocation [RECN_CELL_ID] = 4; + regData->cellColLocation [PAY_CELL_ID] = 5; + regData->cellColLocation [DEP_CELL_ID] = 6; + + switch(regData->type) { case BANK: case CASH: @@ -982,46 +1895,140 @@ regWindow( Widget parent, Account *acc ) case INCOME: case EXPENSE: case EQUITY: - acc->columnLocation [DATE_COL_ID] = 0; - acc->columnLocation [NUM_COL_ID] = 1; - acc->columnLocation [DESC_COL_ID] = 2; - acc->columnLocation [RECN_COL_ID] = 3; - acc->columnLocation [PAY_COL_ID] = 4; - acc->columnLocation [DEP_COL_ID] = 5; - acc->columnLocation [BALN_COL_ID] = 6; - acc -> numCols = 7; + regData->cellColLocation [XTO_CELL_ID] = -1; + regData->cellColLocation [PRCC_CELL_ID] = -1; + regData->cellColLocation [PRCD_CELL_ID] = -1; + regData->cellColLocation [SHRS_CELL_ID] = -1; + regData->cellColLocation [VDEB_CELL_ID] = -1; + regData->cellColLocation [VCRD_CELL_ID] = -1; + regData->cellColLocation [BALN_CELL_ID] = 7; + regData -> numCols = 8; + break; + case STOCK: + case MUTUAL: + regData->cellColLocation [XTO_CELL_ID] = -1; + regData->cellColLocation [PRCC_CELL_ID] = 7; + regData->cellColLocation [PRCD_CELL_ID] = -1; + regData->cellColLocation [VDEB_CELL_ID] = -1; + regData->cellColLocation [VCRD_CELL_ID] = 8; + regData->cellColLocation [SHRS_CELL_ID] = 9; + regData->cellColLocation [BALN_CELL_ID] = 10; + regData -> numCols = 11; break; + + case INC_LEDGER: + case GEN_LEDGER: + regData->cellColLocation [XTO_CELL_ID] = 2; + regData->cellColLocation [PRCC_CELL_ID] = -1; + regData->cellColLocation [PRCD_CELL_ID] = -1; + regData->cellColLocation [SHRS_CELL_ID] = -1; + regData->cellColLocation [VDEB_CELL_ID] = -1; + regData->cellColLocation [VCRD_CELL_ID] = -1; + regData->cellColLocation [BALN_CELL_ID] = 7; + regData -> numCols = 8; + break; + case PORTFOLIO: + regData->cellColLocation [XTO_CELL_ID] = 2; + regData->cellColLocation [PRCC_CELL_ID] = 7; + regData->cellColLocation [PRCD_CELL_ID] = 7; + regData->cellColLocation [VDEB_CELL_ID] = 8; + regData->cellColLocation [VCRD_CELL_ID] = 8; + regData->cellColLocation [SHRS_CELL_ID] = 9; + regData->cellColLocation [BALN_CELL_ID] = 10; + regData -> numCols = 11; + break; + + default: + fprintf( stderr, "Internal Error: Account type: %d is unknown!\n", + regData->type); + } + /* ----------------------------------- */ + /* define where each row shows up, The number on the + * right hand side is the physical location of the cell */ + + regData->cellRowLocation [DATE_CELL_ID] = 0; + regData->cellRowLocation [YEAR_CELL_ID] = 1; + regData->cellRowLocation [NUM_CELL_ID] = 0; + regData->cellRowLocation [ACTN_CELL_ID] = 1; + regData->cellRowLocation [XFRM_CELL_ID] = 0; + regData->cellRowLocation [XTO_CELL_ID] = 1; + regData->cellRowLocation [DESC_CELL_ID] = 0; + regData->cellRowLocation [MEMO_CELL_ID] = 1; + regData->cellRowLocation [RECN_CELL_ID] = 0; + regData->cellRowLocation [PAY_CELL_ID] = 0; + regData->cellRowLocation [DEP_CELL_ID] = 0; + regData->cellRowLocation [VCRD_CELL_ID] = 0; + regData->cellRowLocation [BALN_CELL_ID] = 0; + + switch(regData->type) + { + case BANK: + case CASH: + case ASSET: + case CREDIT: + case LIABILITY: + case INCOME: + case EXPENSE: + case EQUITY: + regData->cellRowLocation [XTO_CELL_ID] = -1; + regData->cellRowLocation [PRCC_CELL_ID] = -1; + regData->cellRowLocation [PRCD_CELL_ID] = -1; + regData->cellRowLocation [SHRS_CELL_ID] = -1; + regData->cellRowLocation [VDEB_CELL_ID] = -1; + regData->cellRowLocation [VCRD_CELL_ID] = -1; + break; + + case STOCK: case MUTUAL: - acc->columnLocation [DATE_COL_ID] = 0; - acc->columnLocation [NUM_COL_ID] = 1; - acc->columnLocation [ACTN_COL_ID] = 2; - acc->columnLocation [DESC_COL_ID] = 3; - acc->columnLocation [RECN_COL_ID] = 4; - acc->columnLocation [PAY_COL_ID] = 5; - acc->columnLocation [DEP_COL_ID] = 6; - acc->columnLocation [PRIC_COL_ID] = 7; - acc->columnLocation [SHRS_COL_ID] = 8; - acc->columnLocation [BALN_COL_ID] = 9; - acc -> numCols = 10; + regData->cellRowLocation [XTO_CELL_ID] = -1; + regData->cellRowLocation [PRCC_CELL_ID] = 0; + regData->cellRowLocation [PRCD_CELL_ID] = -1; + regData->cellRowLocation [SHRS_CELL_ID] = 0; + regData->cellRowLocation [VDEB_CELL_ID] = -1; + regData->cellRowLocation [VCRD_CELL_ID] = 0; + break; + + case INC_LEDGER: + case GEN_LEDGER: + regData->cellRowLocation [XTO_CELL_ID] = 1; + regData->cellRowLocation [DEP_CELL_ID] = 1; /* shift credit down */ + regData->cellRowLocation [BALN_CELL_ID] = 1; /* shift balance down */ + regData->cellRowLocation [PRCC_CELL_ID] = -1; + regData->cellRowLocation [PRCD_CELL_ID] = -1; + regData->cellRowLocation [SHRS_CELL_ID] = -1; + regData->cellRowLocation [VDEB_CELL_ID] = -1; + regData->cellRowLocation [VCRD_CELL_ID] = -1; break; + + case PORTFOLIO: + regData->cellRowLocation [XTO_CELL_ID] = 1; + regData->cellRowLocation [DEP_CELL_ID] = 1; /* shift credit down */ + regData->cellRowLocation [PRCD_CELL_ID] = 0; + regData->cellRowLocation [PRCC_CELL_ID] = 1; /* shift credit down */ + regData->cellRowLocation [SHRS_CELL_ID] = 1; + regData->cellRowLocation [VDEB_CELL_ID] = 0; /* debit on top */ + regData->cellRowLocation [VCRD_CELL_ID] = 1; /* credit on bottom */ + regData->cellRowLocation [BALN_CELL_ID] = 1; /* shift balance down */ + break; + default: - fprintf( stderr, "Internal Error: Account type: %d is unknown!\n", acc->type); } /* ----------------------------------- */ /* set up column widths */ - acc -> colWidths[DATE_CELL_C] = 5; /* also YEAR_CELL_C */ - acc -> colWidths[NUM_CELL_C] = 8; /* also XFER_CELL_C */ - acc -> colWidths[DESC_CELL_C] = 35; /* also MEMO_CELL_C */ - acc -> colWidths[RECN_CELL_C] = 1; /* the widths of columns */ - acc -> colWidths[PAY_CELL_C] = 12; /* the widths of columns */ - acc -> colWidths[DEP_CELL_C] = 12; /* the widths of columns */ - acc -> colWidths[BALN_CELL_C] = 12; /* dollar balance */ + regData -> columnWidths[DATE_CELL_C] = 5; /* also YEAR_CELL_C */ + regData -> columnWidths[NUM_CELL_C] = 6; /* also ACTN_CELL_C */ + regData -> columnWidths[XFRM_CELL_C] = 14; /* also XTO_CELL_C */ + regData -> columnWidths[DESC_CELL_C] = 30; /* also MEMO_CELL_C */ + regData -> columnWidths[RECN_CELL_C] = 1; /* the widths of columns */ + regData -> columnWidths[PAY_CELL_C] = 12; /* the widths of columns */ + regData -> columnWidths[DEP_CELL_C] = 12; /* the widths of columns */ + regData -> columnWidths[BALN_CELL_C] = 12; /* dollar balance */ - switch(acc->type) + switch(regData->type) { case BANK: case CASH: @@ -1031,27 +2038,32 @@ regWindow( Widget parent, Account *acc ) case INCOME: case EXPENSE: case EQUITY: + case GEN_LEDGER: + case INC_LEDGER: break; - case PORTFOLIO: + case STOCK: case MUTUAL: - acc -> colWidths[PRIC_CELL_C] = 8; /* price */ - acc -> colWidths[SHRS_CELL_C] = 8; /* share balance */ - acc -> colWidths[ACTN_CELL_C] = 6; /* action (Buy/Sell)*/ + case PORTFOLIO: + regData -> columnWidths[PRCC_CELL_C] = 8; /* price */ + regData -> columnWidths[SHRS_CELL_C] = 10; /* share balance */ + regData -> columnWidths[VCRD_CELL_C] = 10; /* transaction value */ break; } /* ----------------------------------- */ /* set up column alignments */ - acc -> alignments[DATE_CELL_C] = XmALIGNMENT_END; - acc -> alignments[NUM_CELL_C] = XmALIGNMENT_BEGINNING; /* need XFER to be visible */ - acc -> alignments[DESC_CELL_C] = XmALIGNMENT_BEGINNING; - acc -> alignments[RECN_CELL_C] = XmALIGNMENT_CENTER; - acc -> alignments[PAY_CELL_C] = XmALIGNMENT_END; - acc -> alignments[DEP_CELL_C] = XmALIGNMENT_END; - acc -> alignments[BALN_CELL_C] = XmALIGNMENT_END; - - switch(acc->type) + regData -> alignments[DATE_CELL_C] = XmALIGNMENT_END; + /* ACTN is in NUM_CELL, and needs to be visible */ + regData -> alignments[NUM_CELL_C] = XmALIGNMENT_BEGINNING; + regData -> alignments[XFRM_CELL_C] = XmALIGNMENT_BEGINNING; + regData -> alignments[DESC_CELL_C] = XmALIGNMENT_BEGINNING; + regData -> alignments[RECN_CELL_C] = XmALIGNMENT_CENTER; + regData -> alignments[PAY_CELL_C] = XmALIGNMENT_END; + regData -> alignments[DEP_CELL_C] = XmALIGNMENT_END; + regData -> alignments[BALN_CELL_C] = XmALIGNMENT_END; + + switch(regData->type) { case BANK: case CASH: @@ -1061,29 +2073,40 @@ regWindow( Widget parent, Account *acc ) case INCOME: case EXPENSE: case EQUITY: + case GEN_LEDGER: + case INC_LEDGER: break; - case PORTFOLIO: + case STOCK: case MUTUAL: - acc -> alignments[PRIC_CELL_C] = XmALIGNMENT_END; /* price */ - acc -> alignments[SHRS_CELL_C] = XmALIGNMENT_END; /* share balance */ - acc -> alignments[ACTN_CELL_C] = XmALIGNMENT_BEGINNING; /* action */ + case PORTFOLIO: + regData -> alignments[PRCC_CELL_C] = XmALIGNMENT_END; /* price */ + regData -> alignments[SHRS_CELL_C] = XmALIGNMENT_END; /* share baln */ + regData -> alignments[VCRD_CELL_C] = XmALIGNMENT_END; /* trans value */ break; } /* ----------------------------------- */ /* Put the appropriate heading names in the column titles */ - for (i=0; i<3; i++) { - for (j=0; jrows[i][j] = ""; + for (i=0; i<(NUM_HEADER_ROWS+NUM_ROWS_PER_TRANS); i++) { + for (j=0; jcolumnLabels[i][j] = ""; } } - acc -> rows[0][DATE_CELL_C] = "Date"; - acc -> rows[0][NUM_CELL_C] = "Num"; - acc -> rows[0][DESC_CELL_C] = "Description"; - acc -> rows[0][BALN_CELL_C] = "Balance"; - switch(acc->type) + regData -> columnLabels[0][DATE_CELL_C] = "Date"; + regData -> columnLabels[0][NUM_CELL_C] = "Num"; + regData -> columnLabels[0][XFRM_CELL_C] = "Transfer From"; + regData -> columnLabels[0][DESC_CELL_C] = "Description"; + regData -> columnLabels[0][BALN_CELL_C] = "Balance"; + + if (1 < NUM_HEADER_ROWS) { + regData -> columnLabels[1][ACTN_CELL_C] = "Action"; + regData -> columnLabels[1][XTO_CELL_C] = "Transfer To"; + regData -> columnLabels[1][MEMO_CELL_C] = "Memo"; + } + + switch(regData->type) { case BANK: case CASH: @@ -1093,72 +2116,88 @@ regWindow( Widget parent, Account *acc ) case INCOME: case EXPENSE: case EQUITY: + case GEN_LEDGER: + case INC_LEDGER: break; - case PORTFOLIO: + case STOCK: case MUTUAL: - acc -> rows[0][PRIC_CELL_C] = "Price"; - acc -> rows[0][SHRS_CELL_C] = "Tot Shrs"; - acc -> rows[0][ACTN_CELL_C] = "Action"; /* action */ + case PORTFOLIO: + regData -> columnLabels[0][PRCC_CELL_C] = "Price"; + regData -> columnLabels[0][SHRS_CELL_C] = "Tot Shrs"; + regData -> columnLabels[0][VCRD_CELL_C] = "Value"; break; } - switch(acc->type) + switch(regData->type) { case BANK: - acc -> rows[0][PAY_CELL_C] = "Payment"; - acc -> rows[0][DEP_CELL_C] = "Deposit"; + regData -> columnLabels[0][PAY_CELL_C] = "Payment"; + regData -> columnLabels[0][DEP_CELL_C] = "Deposit"; break; case CASH: - acc -> rows[0][PAY_CELL_C] = "Spend"; - acc -> rows[0][DEP_CELL_C] = "Receive"; + regData -> columnLabels[0][PAY_CELL_C] = "Spend"; + regData -> columnLabels[0][DEP_CELL_C] = "Receive"; break; case ASSET: - acc -> rows[0][PAY_CELL_C] = "Decrease"; - acc -> rows[0][DEP_CELL_C] = "Increase"; + regData -> columnLabels[0][PAY_CELL_C] = "Depreciation"; + regData -> columnLabels[0][DEP_CELL_C] = "Appreciation"; break; case CREDIT: - acc -> rows[0][PAY_CELL_C] = "Charge"; - acc -> rows[0][DEP_CELL_C] = "Payment"; + regData -> columnLabels[0][PAY_CELL_C] = "Charge"; + regData -> columnLabels[0][DEP_CELL_C] = "Payment"; break; case LIABILITY: - acc -> rows[0][PAY_CELL_C] = "Increase"; - acc -> rows[0][DEP_CELL_C] = "Decrease"; + regData -> columnLabels[0][PAY_CELL_C] = "Increase"; + regData -> columnLabels[0][DEP_CELL_C] = "Decrease"; break; case INCOME: - acc -> rows[0][PAY_CELL_C] = "Income"; - acc -> rows[0][DEP_CELL_C] = "Charge"; + regData -> columnLabels[0][PAY_CELL_C] = "Income"; + regData -> columnLabels[0][DEP_CELL_C] = "Charge"; break; case EXPENSE: - acc -> rows[0][PAY_CELL_C] = "Rebate"; - acc -> rows[0][DEP_CELL_C] = "Expense"; + regData -> columnLabels[0][PAY_CELL_C] = "Rebate"; + regData -> columnLabels[0][DEP_CELL_C] = "Expense"; break; case EQUITY: - acc -> rows[0][PAY_CELL_C] = "Surplus"; - acc -> rows[0][DEP_CELL_C] = "Deficit"; + regData -> columnLabels[0][PAY_CELL_C] = "Surplus"; + regData -> columnLabels[0][DEP_CELL_C] = "Deficit"; break; - case PORTFOLIO: + case STOCK: case MUTUAL: - acc -> rows[0][PAY_CELL_C] = "Sold"; - acc -> rows[0][DEP_CELL_C] = "Bought"; + case PORTFOLIO: + regData -> columnLabels[0][PAY_CELL_C] = "Sold"; + regData -> columnLabels[0][DEP_CELL_C] = "Bought"; + break; + case GEN_LEDGER: + regData -> columnLabels[0][PAY_CELL_C] = "Debit"; + regData -> columnLabels[0][DEP_CELL_C] = "Credit"; + break; + case INC_LEDGER: + regData -> columnLabels[0][PAY_CELL_C] = "Credit"; + regData -> columnLabels[0][DEP_CELL_C] = "Debit"; break; } - data = (String **)XtMalloc(3*sizeof(String *)); - data[0] = &(acc -> rows[0][0]); - data[1] = &(acc -> rows[1][0]); - data[2] = &(acc -> rows[2][0]); + data = (String **)XtMalloc( + (NUM_HEADER_ROWS+NUM_ROWS_PER_TRANS) * + sizeof(String *)); + + for (i=0; i<(NUM_HEADER_ROWS+NUM_ROWS_PER_TRANS); i++) { + data[i] = &(regData -> columnLabels[i][0]); + } + sprintf( buf, "reg" ); - reg = XtVaCreateWidget( strcat(buf,accRes[acc->type]), + reg = XtVaCreateWidget( strcat(buf,accRes[regData->type]), xbaeMatrixWidgetClass, frame, XmNcells, data, - XmNfixedRows, 1, + XmNfixedRows, NUM_HEADER_ROWS, XmNfixedColumns, 0, - XmNrows, 2, + XmNrows, NUM_HEADER_ROWS+NUM_ROWS_PER_TRANS, XmNvisibleRows, 15, XmNfill, True, - XmNcolumns, acc -> numCols, - XmNcolumnWidths, acc -> colWidths, - XmNcolumnAlignments, acc -> alignments, + XmNcolumns, regData -> numCols, + XmNcolumnWidths, regData -> columnWidths, + XmNcolumnAlignments, regData -> alignments, XmNtraverseFixedCells, False, XmNgridType, XmGRID_SHADOW_IN, XmNshadowType, XmSHADOW_ETCHED_IN, @@ -1185,8 +2224,12 @@ regWindow( Widget parent, Account *acc ) /* create the xfer account box for the first time */ /* but first, find the topmost group */ - grp = xaccGetRootGroupOfAcct (acc); - regData->xferbox = xferBox (reg, grp); + { + AccountGroup *grp; + grp = xaccGetRootGroupOfAcct (regData->blackacc[0]); + regData->xfrmbox = xferBox (reg, grp); + regData->xtobox = xferBox (reg, grp); + } /******************************************************************\ * The button area... also contains balance fields * @@ -1247,7 +2290,7 @@ regWindow( Widget parent, Account *acc ) XtAddCallback( widget, XmNactivateCallback, destroyShellCB, (XtPointer)(regData->dialog) ); - position+=2; + position += NUM_ROWS_PER_TRANS; /* Fix button area of the buttonform to its current size, and not let * it resize. */ @@ -1305,8 +2348,9 @@ regWindow( Widget parent, Account *acc ) XtPopup( regData->dialog, XtGrabNone ); /* unmanage the ComboBoxes, until they are needed */ - SetPopBox (regData->actbox, -1, -1); - SetPopBox (regData->xferbox, -1, -1); + SetPopBox (regData->actbox, -1, -1); + SetPopBox (regData->xfrmbox, -1, -1); + SetPopBox (regData->xtobox, -1, -1); unsetBusyCursor( parent ); @@ -1323,22 +2367,27 @@ regWindow( Widget parent, Account *acc ) * cb - * * Return: none * \********************************************************************/ -void +static void closeRegWindow( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; - Account *acc; - - acc = regData->acc; /* Save any unsaved changes */ XbaeMatrixCommitEdit( regData->reg, False ); - regSaveTransaction( regData, regData->lastTrans ); + regSaveTransaction( regData, regData->currEntry ); - /* hack alert -- free the ComboBox popup boxes data structures too */ + /* free combo-box data too */ + freePopBox (regData->actbox); + freePopBox (regData->xfrmbox); + freePopBox (regData->xtobox); + if (1 >= regData ->numAcc) { + regData->blackacc[0]->regData = NULL; + } else { + regData->blackacc[0]->regLedger = NULL; + } + ledgerListRemoveList (regData->blackacc, regData); _free(regData); - acc->regData = NULL; DEBUG("closed RegWindow\n"); } @@ -1352,12 +2401,21 @@ closeRegWindow( Widget mw, XtPointer cd, XtPointer cb ) * cb - * * Return: none * \********************************************************************/ -void startAdjBCB( Widget mw, XtPointer cd, XtPointer cb ) +static void +startAdjBCB( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; + Account *acc; - if( regData->acc->adjBData == NULL ) - regData->acc->adjBData = adjBWindow( toplevel, regData->acc ); + /* Must have number of accounts be one. If not one, + * then this callback should never have been called, + * since the menu entry is supposed to be greyed out. + */ + if (1 != regData->numAcc) return; + + acc = regData->blackacc[0]; + if( acc->adjBData == NULL ) + acc->adjBData = adjBWindow( toplevel, acc ); } /********************************************************************\ @@ -1369,12 +2427,21 @@ void startAdjBCB( Widget mw, XtPointer cd, XtPointer cb ) * cb - * * Return: none * \********************************************************************/ -void startRecnCB( Widget mw, XtPointer cd, XtPointer cb ) +static void +startRecnCB( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; + Account *acc; - if( regData->acc->recnData == NULL ) - regData->acc->recnData = recnWindow( toplevel, regData->acc ); + /* Must have number of accounts be one. If not one, + * then this callback should never have been called, + * since the menu entry is supposed to be greyed out. + */ + if (1 != regData->numAcc) return; + + acc = regData->blackacc[0]; + if( acc->recnData == NULL ) + acc->recnData = recnWindow( toplevel, acc ); } /********************************************************************\ @@ -1385,13 +2452,13 @@ void startRecnCB( Widget mw, XtPointer cd, XtPointer cb ) * cb - * * Return: none * \********************************************************************/ -void +static void recordCB( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; XbaeMatrixCommitEdit( regData->reg, False ); - regSaveTransaction( regData, regData->lastTrans ); + regSaveTransaction( regData, regData->currEntry ); } /********************************************************************\ @@ -1403,23 +2470,31 @@ recordCB( Widget mw, XtPointer cd, XtPointer cb ) * Return: none * \********************************************************************/ -void +static void deleteCB( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; - Account *acc = regData->acc; Transaction *trans; + int currow; - trans = getTransaction (acc, regData->lastTrans ); + currow = NUM_ROWS_PER_TRANS *regData->currEntry + NUM_HEADER_ROWS; + trans = (Transaction *) XbaeMatrixGetRowUserData (regData->reg, currow); + if( NULL != trans) { - char *msg = TRANS_DEL_MSG; + char buf[BUFSIZE]; + sprintf (buf, TRANS_DEL_MSG, trans->description); - if( verifyBox( toplevel, msg ) ) + if( verifyBox( toplevel, buf ) ) { Account * cred = (Account *) (trans->credit); Account * deb = (Account *) (trans->debit); + /* unmanage the ComboBoxes, otherwise things get confusing */ + SetPopBox (regData->actbox, -1, -1); + SetPopBox (regData->xfrmbox, -1, -1); + SetPopBox (regData->xtobox, -1, -1); + /* remove the transaction from both accounts */ REMOVE_TRANS (cred, trans); REMOVE_TRANS (deb, trans); @@ -1427,6 +2502,9 @@ deleteCB( Widget mw, XtPointer cd, XtPointer cb ) RECALC_BALANCE (deb); RECALC_BALANCE (cred); + REFRESH_REGISTER (deb); + REFRESH_REGISTER (cred); + /* Delete the transaction */ freeTransaction (trans); } @@ -1441,7 +2519,7 @@ deleteCB( Widget mw, XtPointer cd, XtPointer cb ) * cb - * * Return: none * \********************************************************************/ -void +static void cancelCB( Widget mw, XtPointer cd, XtPointer cb ) { RegWindow *regData = (RegWindow *)cd; @@ -1460,7 +2538,7 @@ cancelCB( Widget mw, XtPointer cd, XtPointer cb ) * cb - * * Return: none * \********************************************************************/ -void +static void regCB( Widget mw, XtPointer cd, XtPointer cb ) { int row,col; @@ -1471,8 +2549,6 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) (XbaeMatrixDefaultActionCallbackStruct *)cb; RegWindow *regData = (RegWindow *)cd; - Account *acc = regData->acc; - reg = regData->reg; row = cbs->row; @@ -1485,29 +2561,35 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) DEBUGCMD(printf(" row = %d\n col = %d\n",row,col)); /* figure out if we are editing a different transaction... if we * are, then we need to save the transaction we left */ - if( regData->lastTrans != (row-1)/2 ) + if( regData->currEntry != (row-NUM_HEADER_ROWS)/NUM_ROWS_PER_TRANS ) { DEBUG("Save Transaction\n"); - DEBUGCMD(printf(" lastTrans = %d\n currTrans = %d\n", - regData->lastTrans, (row+1)/2 )); + DEBUGCMD(printf(" currEntry = %d\n currTrans = %d\n", + regData->currEntry, + (row+NUM_HEADER_ROWS)/NUM_ROWS_PER_TRANS)); - regSaveTransaction( regData, regData->lastTrans ); + regSaveTransaction( regData, regData->currEntry ); - regData->lastTrans = (row-1)/2; + regData->currEntry = (row-NUM_HEADER_ROWS)/NUM_ROWS_PER_TRANS; regData->insert = 0; - regData->qf = acc->qfRoot; + + /* hack alert -- quick-fill mostly broken for ledgers */ + if (1 == regData->numAcc) { + Account *acc = regData->blackacc[0]; + regData->qf = acc->qfRoot; + } else { + regData->qf = NULL; + } } /* If this is a cell which isn't editible, then don't * let the user enter! */ - if( !IN_DATE_CELL(row,col) && !IN_NUM_CELL(row,col) && - !IN_DESC_CELL(row,col) && !IN_PAY_CELL(row,col) && - !IN_RECN_CELL(row,col) && !IN_DEP_CELL(row,col) && - !IN_XFER_CELL(row,col) && - !((PORTFOLIO == acc->type) && IN_PRIC_CELL(row,col)) && - !((MUTUAL == acc->type) && IN_PRIC_CELL(row,col)) && - !((PORTFOLIO == acc->type) && IN_ACTN_CELL(row,col)) && - !((MUTUAL == acc->type) && IN_ACTN_CELL(row,col)) && + if( !IN_DATE_CELL(row,col) && !IN_NUM_CELL (row,col) && + !IN_DESC_CELL(row,col) && !IN_PAY_CELL (row,col) && + !IN_RECN_CELL(row,col) && !IN_DEP_CELL (row,col) && + !IN_XFRM_CELL(row,col) && !IN_XTO_CELL (row,col) && + !IN_ACTN_CELL(row,col) && + !IN_PRCC_CELL(row,col) && !IN_PRCD_CELL(row,col) && !IN_MEMO_CELL(row,col) && !IN_YEAR_CELL(row,col)) { ((XbaeMatrixEnterCellCallbackStruct *)cbs)->doit = FALSE; @@ -1522,10 +2604,11 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) XtVaGetValues( mw, XmNcells, &data, NULL ); DEBUGCMD(printf("data[%d][RECN_CELL_C] = %s\n", row, data[row][RECN_CELL_C])); - if( data[row][RECN_CELL_C][0] == NREC ) + if( data[row][RECN_CELL_C][0] == NREC ) { data[row][RECN_CELL_C][0] = CREC; - else + } else { data[row][RECN_CELL_C][0] = NREC; + } /* this cell has been modified, so we need to save when we * leave!!! */ @@ -1536,18 +2619,24 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) } /* otherwise, move the ACTN widget */ - else if( ((PORTFOLIO == acc->type) && IN_ACTN_CELL(row,col)) || - ((MUTUAL == acc->type) && IN_ACTN_CELL(row,col)) ) + else if( IN_ACTN_CELL(row,col)) { SetPopBox (regData->actbox, row, col); regData->changed |= MOD_ACTN; } - /* otherwise, move the XFER widget */ - else if( IN_XFER_CELL(row,col) ) + /* otherwise, move the XFRM widget */ + else if( IN_XFRM_CELL(row,col) ) + { + SetPopBox (regData->xfrmbox, row, col); + regData->changed |= MOD_XFRM; + } + + /* otherwise, move the XTO widget */ + else if( IN_XTO_CELL(row,col) ) { - SetPopBox (regData->xferbox, row, col); - regData->changed |= MOD_XFER; + SetPopBox (regData->xtobox, row, col); + regData->changed |= MOD_XTO; } break; } @@ -1565,7 +2654,12 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) * first time by looking at regData->changed */ if( regData->changed == MOD_NONE ) { - Transaction *trans = getTransaction( acc, regData->lastTrans ); + int currow; + Transaction *trans; + + currow = NUM_ROWS_PER_TRANS * regData->currEntry + NUM_HEADER_ROWS; + trans = (Transaction *) XbaeMatrixGetRowUserData (regData->reg, currow); + /* If trans==NULL, then this must be a new transaction */ if( (trans != NULL) && (trans->reconciled == YREC) ) { @@ -1657,9 +2751,8 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) /* look to see if numeric format is OK. Note that * the share price cell exists only for certain account types */ - if( IN_PAY_CELL(row,col) || IN_DEP_CELL(row,col) || - ((PORTFOLIO == acc->type) && IN_PRIC_CELL(row,col)) || - ((MUTUAL == acc->type) && IN_PRIC_CELL(row,col)) ) + if( IN_PAY_CELL (row,col) || IN_DEP_CELL(row,col) || + IN_PRCC_CELL(row,col) || IN_PRCD_CELL(row,col) ) { /* text pointer is NULL if non-alpha key hit */ /* for example, the delete key */ @@ -1708,24 +2801,26 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) if( IN_MEMO_CELL(row,col) ) regData->changed |= MOD_MEMO; - if( ((PORTFOLIO == acc->type) && IN_PRIC_CELL(row,col)) || - ((MUTUAL == acc->type) && IN_PRIC_CELL(row,col)) ) + if( IN_PRCC_CELL(row,col) || IN_PRCD_CELL(row,col) ) regData->changed |= MOD_PRIC; /* Note: for cell widgets, this callback will never * indicate a row,col with a cell widget in it. * Thus, the following if statment will never be true */ - if( ((PORTFOLIO == acc->type) && IN_ACTN_CELL(row,col)) || - ((MUTUAL == acc->type) && IN_ACTN_CELL(row,col)) ) { + if( IN_ACTN_CELL(row,col)) { regData->changed |= MOD_ACTN; } /* Note: for cell widgets, this callback will never * indicate a row,col with a cell widget in it. * Thus, the following if statment will never be true */ - if( IN_XFER_CELL(row,col) ) - regData->changed |= MOD_XFER; + if( IN_XFRM_CELL(row,col) ) { + regData->changed |= MOD_XFRM; + } + if( IN_XTO_CELL(row,col) ) { + regData->changed |= MOD_XTO; + } break; @@ -1750,6 +2845,8 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) tcbs->next_column = PAY_CELL_C; if( regData->qf != NULL ) if( regData->qf->trans != NULL ) { + /* hack alert -- this is guarenteed to break for ledgers */ + Account *acc = regData->blackacc[0]; double themount; themount = xaccGetAmount (acc, regData->qf->trans); if( 0.0 > themount ) @@ -1781,6 +2878,8 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) tcbs->next_column = DEP_CELL_C; if( regData->qf != NULL ) if( regData->qf->trans != NULL ) { + /* hack alert -- this is guarenteed to break for ledgers */ + Account *acc = regData->blackacc[0]; double themount; themount = xaccGetAmount (acc, regData->qf->trans); if( 0.0 <= themount ) @@ -1830,7 +2929,7 @@ regCB( Widget mw, XtPointer cd, XtPointer cb ) * mvcbs - the modify-verify callback struct (from regCB) * * Return: none * \********************************************************************/ -void +static void dateCellFormat( Widget mw, XbaeMatrixModifyVerifyCallbackStruct *mvcbs, int do_year ) { /* Date format -- valid characters are numerals, '/', and diff --git a/src/Transaction.c b/src/Transaction.c index 2e20a07ee2..9ac4319328 100644 --- a/src/Transaction.c +++ b/src/Transaction.c @@ -23,6 +23,7 @@ * Huntington Beach, CA 92648-4632 * \********************************************************************/ +#include "date.h" #include "Transaction.h" #include "util.h" @@ -118,6 +119,53 @@ freeTransaction( Transaction *trans ) _free(trans); } +/********************************************************************\ + * sorting comparison function + * + * returns a negative value if transaction a is dated earlier than b, + * returns a positive value if transaction a is dated later than b, + * returns zero if both transactions are on the same date. + * +\********************************************************************/ + +int +xaccTransOrder (Transaction **ta, Transaction **tb) +{ + int retval; + char *da, *db; + + retval = datecmp (&((*ta)->date), &((*tb)->date)); + + /* if dates differ, return */ + if (retval) return retval; + + /* otherwise, sort on transaction strings */ + da = (*ta)->description; + db = (*tb)->description; + if (!da) return -1; + if (!db) return +1; + + retval = strcmp (da, db); + return retval; +} + +/********************************************************************\ +\********************************************************************/ + +int +xaccCountTransactions (Transaction **tarray) +{ + Transaction *trans; + int ntrans = 0; + + trans = tarray[0]; + while (trans) { + ntrans ++; + trans = tarray[ntrans]; + } + return ntrans; +} + /************************ END OF ************************************\ \************************* FILE *************************************/ diff --git a/src/XferWindow.c b/src/XferWindow.c index ecc5d0d1ce..5de015aa37 100644 --- a/src/XferWindow.c +++ b/src/XferWindow.c @@ -251,6 +251,7 @@ xferWindow( Widget parent ) accountMenu[i].mnemonic = 0; accountMenu[i].accelerator = NULL; accountMenu[i].accel_text = NULL; + accountMenu[i].sensitive = True; accountMenu[i].callback = menuCB; accountMenu[i].callback_data = xferData->menuData[2*i]; accountMenu[i].subitems = (MenuItem *)NULL; @@ -284,6 +285,7 @@ xferWindow( Widget parent ) accountMenu[i].mnemonic = 0; accountMenu[i].accelerator = NULL; accountMenu[i].accel_text = NULL; + accountMenu[i].sensitive = True; accountMenu[i].callback = menuCB; accountMenu[i].callback_data = xferData->menuData[2*i+1]; accountMenu[i].subitems = (MenuItem *)NULL; @@ -487,4 +489,4 @@ xferCB( Widget mw, XtPointer cd, XtPointer cb ) XtDestroyWidget(xferData->dialog); } -/* ********************** END OF FILE *************************/ +/*********************** END OF FILE *************************/ diff --git a/xacc.lsm b/xacc.lsm index e253033375..cd34b53365 100644 --- a/xacc.lsm +++ b/xacc.lsm @@ -1,7 +1,7 @@ Begin3 Title: xacc -- simple single-user accounting program -Version: 0.9p -Entered-date: 21NOV97 +Version: 0.9t +Entered-date: 24NOV97 Description: xacc is a program to keep track of your finances. It offers a simple check-book register style interface. Currently, the features are quite simple: @@ -19,15 +19,24 @@ Description: xacc is a program to keep track of your finances. It the previous transaction. Handy if you have similar transactions on a regular basis. (such as depositing your paycheck every week :) - - General Ledger. Well, not yet, but double-entry - is supported (transfers between accounts), - sub-accounts are supported (so that master accounts - can contain detail accounts), as well as - income/expense accounts that handle double-entry - properly. + - Stock/Mutual Fund Portfolios. Tracks stock + investments. + - General Ledger. This is an advanced feature + for more serious work. Supported are: + o Double Entry: Transactions can appear in two + accounts, one debited, one credited. + o Sub-accounts: A master account can have + a heriarchy of detail accounts. + o Income/Expense account types. + o General Ledger for displaying multiple + accounts at once. This is a beta development version with known minor user interface bugs and no known major bugs. + Under rare circumstances, it will core dump (due to + Motif problems) so save often! It has been tested + only lightly but seems to work. It is not beleived to + corrupt data. Keywords: X11 Accounting double entry Author: rclark@cs.hmc.edu (Robin Clark) @@ -35,7 +44,7 @@ Maintained-by: linas@linas.org (Linas Vepstas) Primary-site: http://www3.hmc.edu/~rclark/xacc/ Alternate-site: http://linas.org/linux/xacc Alternate-site: sunsite.unc.edu /pub/Linux/apps/financial/accounting/ - 521kB xacc-0.9m.tar.gz + 527kB xacc-0.9s.tar.gz Platforms: Linux/Unix/X Windows/Lesstif/Motif/Sparc/Intel Copying-policy: GPL End