2001-06-17 Joshua Sled <jsled@asynchronous.org>

* src/engine/gnc-account-xml-v2.c (dom_tree_to_account): Added.

	* src/engine/gnc-transaction-xml-v2.c (dom_tree_to_transaction):
	Added.

	* src/FileDialog.c: Added support for copying
	scheduled-transaction-related GNCBook fields.
	src/engine/

	* src/engine/FreqSpec.{h,c}: Added.

	* src/engine/SchedXaction.{h,c}: Added.

	* src/engine/gnc-{freqspec,schedxaction}-xml-v2.c: Added

	* src/engine/gnc-book.c: Added the template group [template
	transaction belong to the template group] and the Scheduled
	Trasnaction list.

	* src/gnome/glade/sched_xact.glade: Added.

	* src/gnome/dialog-nextrun.{h,c}: Added.

	* src/gnome/dialog-scheduledxaction.{h,c}: Added.

	* src/gnome/gnc-frequency.{h,c}: Added.

	* src/SplitLedger.c, src/MultiLedger.c: Added support for a
	Template Ledger.

	* src/guile/gnc.gwp: Added wrapping for
	dialog-{scheduledxactions,nextrun}-creation functions.

	* src/register/splitreg.c: Added formula credit and debit cells,
	and flags for template-register support.

	* src/register/formulacell.h: Added.

	* src/gnome/gnc-dateedit.h: Added explanatory comment from dave_p
	in IRC.

	* src/gnome/query-user.h: Added prototype of
 	gnc_verify_dialog_parented(...), so I could use it the SX UI
	stuff.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4725 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas
2001-06-18 08:25:46 +00:00
parent 3f921fd1b4
commit d5f96b9ba2
54 changed files with 10209 additions and 95 deletions

View File

@@ -66,6 +66,9 @@ Christian Stimming <stimming@tuhh.de> is a report-generating
monster. He's been on of the largest contributors to the current report
infrastructure, creating many of the important reports.
Joshua Sled <jsled@asynchronous.org> works on scheduled transactions
and budgeting.
Other Contributors:
----------------

View File

@@ -1,3 +1,50 @@
2001-06-17 Joshua Sled <jsled@asynchronous.org>
* src/engine/gnc-account-xml-v2.c (dom_tree_to_account): Added.
* src/engine/gnc-transaction-xml-v2.c (dom_tree_to_transaction):
Added.
* src/FileDialog.c: Added support for copying
scheduled-transaction-related GNCBook fields.
src/engine/
* src/engine/FreqSpec.{h,c}: Added.
* src/engine/SchedXaction.{h,c}: Added.
* src/engine/gnc-{freqspec,schedxaction}-xml-v2.c: Added
* src/engine/gnc-book.c: Added the template group [template
transaction belong to the template group] and the Scheduled
Trasnaction list.
* src/gnome/glade/sched_xact.glade: Added.
* src/gnome/dialog-nextrun.{h,c}: Added.
* src/gnome/dialog-scheduledxaction.{h,c}: Added.
* src/gnome/gnc-frequency.{h,c}: Added.
* src/SplitLedger.c, src/MultiLedger.c: Added support for a
Template Ledger.
* src/guile/gnc.gwp: Added wrapping for
dialog-{scheduledxactions,nextrun}-creation functions.
* src/register/splitreg.c: Added formula credit and debit cells,
and flags for template-register support.
* src/register/formulacell.h: Added.
* src/gnome/gnc-dateedit.h: Added explanatory comment from dave_p
in IRC.
* src/gnome/query-user.h: Added prototype of
gnc_verify_dialog_parented(...), so I could use it the SX UI
stuff.
2001-06-17 Kevin Finn <kevinfinn@mediaone.net>
* src/gnome/window-reconcile.c: Implemented automatic interest
@@ -75,7 +122,7 @@
creation druid code. Rename api calls to reflect the more
general use of the druid.
2001-06-16 Robert Graham Merkel <rgmerk@mira.net
2001-06-16 Robert Graham Merkel <rgmerk@mira.net>
* po/POTFILES.in : remove references to deleted files.

View File

@@ -565,6 +565,8 @@ gncFileSaveAs (void)
{
AccountGroup *group;
GNCPriceDB *pdb;
GList *sxList;
AccountGroup *templateGroup;
GNCBook *new_book;
GNCBook *book;
const char *filename;
@@ -596,8 +598,14 @@ gncFileSaveAs (void)
}
/* -- this session code is NOT identical in FileOpen and FileSaveAs -- */
// FIXME: this might want to be a function of the GNCBook, since it
// needs to sync with changes to the internals/structure of
// GNCBook... --jsled
group = gnc_book_get_group(book);
pdb = gnc_book_get_pricedb(book);
sxList = gnc_book_get_schedxactions(book);
templateGroup = gnc_book_get_template_group(book);
new_book = gnc_book_new ();
gnc_book_begin (new_book, newfile, FALSE, FALSE);
@@ -671,6 +679,8 @@ gncFileSaveAs (void)
/* OK, save the data to the file ... */
gnc_book_set_group(new_book, group);
gnc_book_set_pricedb(new_book, pdb);
gnc_book_set_schedxactions(new_book, sxList);
gnc_book_set_template_group(new_book, templateGroup);
gncFileSave ();
g_free (newfile);

View File

@@ -138,6 +138,8 @@ void gncFileQuit (void);
AccountGroup *gncGetCurrentGroup (void);
Account *gncGetTemplateAccount(void);
GNCBook *gncGetCurrentBook (void);
#endif /* __GNC_FILE_DIALOG_H__ */

View File

@@ -41,6 +41,7 @@
#define REGISTER_SINGLE_CM_CLASS "register-single"
#define REGISTER_SUBACCOUNT_CM_CLASS "register-subaccount"
#define REGISTER_GL_CM_CLASS "register-gl"
#define REGISTER_TEMPLATE_CM_CLASS "register-template"
struct _xaccLedgerDisplay
@@ -74,7 +75,8 @@ static xaccLedgerDisplay *
xaccLedgerDisplayInternal (Account *lead_account, Query *q,
LedgerDisplayType ld_type,
SplitRegisterType reg_type,
SplitRegisterStyle style);
SplitRegisterStyle style,
gboolean templateMode );
static void xaccLedgerDisplayRefreshInternal (xaccLedgerDisplay *ld,
GList *splits);
@@ -358,7 +360,7 @@ xaccLedgerDisplaySimple (Account *account)
reg_type = get_reg_type (account, LD_SINGLE);
return xaccLedgerDisplayInternal (account, NULL, LD_SINGLE, reg_type,
gnc_get_default_register_style ());
gnc_get_default_register_style (), FALSE );
}
/********************************************************************\
@@ -378,9 +380,11 @@ xaccLedgerDisplayAccGroup (Account *account)
reg_type = get_reg_type (account, LD_SUBACCOUNT);
return xaccLedgerDisplayInternal (account, NULL, LD_SUBACCOUNT,
reg_type, REG_STYLE_JOURNAL);
reg_type, REG_STYLE_JOURNAL, FALSE );
}
/********************************************************************\
* xaccLedgerDisplayGL *
* opens up a general ledger window *
@@ -390,7 +394,7 @@ xaccLedgerDisplayAccGroup (Account *account)
\********************************************************************/
xaccLedgerDisplay *
xaccLedgerDisplayGL (void)
xaccLedgerDisplayGL ()
{
Query *query;
time_t start;
@@ -423,7 +427,48 @@ xaccLedgerDisplayGL (void)
return xaccLedgerDisplayInternal (NULL, query, LD_GL,
GENERAL_LEDGER,
REG_STYLE_JOURNAL);
REG_STYLE_JOURNAL, FALSE );
}
/**
* id is some identifier that can be:
* . used in a query to look for the transaction which belong to this
* template ledger
* . set in a specific key value for new transactions which belong to
* this template ledger.
**/
xaccLedgerDisplay *
xaccLedgerDisplayTemplateGL( char *id )
{
GNCBook *book;
Query *q;
time_t start;
struct tm *tm;
xaccLedgerDisplay *ld;
SplitRegister *sr;
AccountGroup *ag;
Account *acct;
q = xaccMallocQuery();
ag = gnc_book_get_template_group( gncGetCurrentBook() );
acct = xaccGetAccountFromName( ag, id );
if ( acct == NULL ) {
// FIXME
printf( "can't get template account for id \"%s\"\n", id );
}
xaccQueryAddSingleAccountMatch( q, acct, QUERY_AND );
book = gncGetCurrentBook();
xaccQuerySetGroup( q, gnc_book_get_template_group(book) );
ld = xaccLedgerDisplayInternal( NULL, q, LD_GL,
GENERAL_LEDGER,
REG_STYLE_JOURNAL,
TRUE ); // template mode? TRUE.
sr = xaccLedgerDisplayGetSR( ld );
sr->templateAcct = acct;
return ld;
}
static gncUIWidget
@@ -541,8 +586,6 @@ refresh_handler (GHashTable *changes, gpointer user_data)
}
}
xaccQuerySetGroup (ld->query, gncGetCurrentGroup ());
splits = xaccQueryGetSplits (ld->query);
ledger_set_watches (ld, splits);
@@ -590,6 +633,7 @@ make_ledger_query (xaccLedgerDisplay *ld,
break;
case LD_GL:
case LD_TEMPLATE:
return;
default:
@@ -637,14 +681,15 @@ xaccLedgerDisplay *
xaccLedgerDisplayQuery (Query *query, SplitRegisterType type,
SplitRegisterStyle style)
{
return xaccLedgerDisplayInternal (NULL, query, LD_GL, type, style);
return xaccLedgerDisplayInternal (NULL, query, LD_GL, type, style, FALSE);
}
static xaccLedgerDisplay *
xaccLedgerDisplayInternal (Account *lead_account, Query *q,
LedgerDisplayType ld_type,
SplitRegisterType reg_type,
SplitRegisterStyle style)
SplitRegisterStyle style,
gboolean templateMode )
{
xaccLedgerDisplay *ld;
gboolean show_all;
@@ -712,9 +757,16 @@ xaccLedgerDisplayInternal (Account *lead_account, Query *q,
break;
case LD_TEMPLATE:
class = REGISTER_TEMPLATE_CM_CLASS;
// FIXME: sanity checks?
// Check for kvp-frame data?
break;
default:
PERR ("bad ledger type: %d", ld_type);
return NULL;
}
ld = g_new (xaccLedgerDisplay, 1);
@@ -748,24 +800,33 @@ xaccLedgerDisplayInternal (Account *lead_account, Query *q,
/* xaccMallocSplitRegister will malloc & initialize the register,
* but will not do the gui init */
view.entry_handler = xaccSRGetEntryHandler;
view.label_handler = xaccSRGetLabelHandler;
view.io_flag_handler = xaccSRGetIOFlagsHandler;
view.fg_color_handler = xaccSRGetFGColorHandler;
view.bg_color_handler = xaccSRGetBGColorHandler;
view.cell_border_handler = xaccSRGetCellBorderHandler;
view.confirm_handler = xaccSRConfirmHandler;
view.handler_user_data = NULL;
// The following handlers are changed in template mode.
if ( templateMode ) {
view.entry_handler = xaccSRTemplateGetEntryHandler;
view.io_flag_handler = xaccSRTemplateGetIOFlagsHandler;
view.confirm_handler = xaccSRTemplateConfirmHandler;
} else {
view.entry_handler = xaccSRGetEntryHandler;
view.io_flag_handler = xaccSRGetIOFlagsHandler;
view.confirm_handler = xaccSRConfirmHandler;
}
ld->reg = xaccMallocSplitRegister (reg_type, style, FALSE, &view,
xaccMLGUIDMalloc,
xaccMLGUIDFree,
xaccMLGUIDCopy);
xaccMLGUIDCopy,
templateMode);
xaccSRSetData (ld->reg, ld,
xaccLedgerDisplayParent,
xaccLedgerDisplaySetHelp);
ld->reg->template = templateMode;
splits = xaccQueryGetSplits (ld->query);
ledger_set_watches (ld, splits);
@@ -825,6 +886,8 @@ xaccLedgerDisplayRefresh (xaccLedgerDisplay *ld)
if (!ld || ld->loading)
return;
//xaccQueryPrint( ld->query );
xaccLedgerDisplayRefreshInternal (ld, xaccQueryGetSplits (ld->query));
}
@@ -859,6 +922,13 @@ xaccLedgerDisplayRefreshByReg (SplitRegister *reg)
xaccLedgerDisplayRefresh (ld);
return;
}
ld = gnc_find_first_gui_component (REGISTER_TEMPLATE_CM_CLASS,
find_by_reg, reg );
if (ld)
{
xaccLedgerDisplayRefresh (ld);
}
}
/********************************************************************\
@@ -892,6 +962,8 @@ xaccDestroyLedgerDisplay (Account *account)
xaccDestroyLedgerDisplayClass (account, REGISTER_SINGLE_CM_CLASS);
xaccDestroyLedgerDisplayClass (account, REGISTER_SUBACCOUNT_CM_CLASS);
xaccDestroyLedgerDisplayClass (account, REGISTER_GL_CM_CLASS);
// no TEMPLATE_CM_CLASS, because it doesn't correspond to any account
// FIXME: but there probably should be an analagous method
}
void

View File

@@ -32,6 +32,7 @@
#include "Query.h"
#include "splitreg.h"
#include "SplitLedger.h"
#include "SchedXaction.h"
#include "Transaction.h"
@@ -50,7 +51,8 @@ typedef enum
{
LD_SINGLE,
LD_SUBACCOUNT,
LD_GL
LD_GL,
LD_TEMPLATE,
} LedgerDisplayType;
@@ -82,6 +84,16 @@ xaccLedgerDisplay * xaccLedgerDisplaySimple (Account *account);
xaccLedgerDisplay * xaccLedgerDisplayAccGroup (Account *account);
xaccLedgerDisplay * xaccLedgerDisplayGL (void);
xaccLedgerDisplay * xaccLedgerDisplayGLTemplate( char *id );
/**
* Displays a template ledger.
* This lists template Splits from the given ScheduledTransaction.
*
* Really, requires a GList of scheduled transactions and kvp-frame
* data.
**/
xaccLedgerDisplay * xaccLedgerDisplayTemplateGL( );
/* display a general ledger for an arbitrary query */
xaccLedgerDisplay * xaccLedgerDisplayQuery (Query *query,

View File

@@ -195,6 +195,7 @@ struct _SRInfo
/* hook to set help string */
SRSetHelpCallback set_help;
};
@@ -230,8 +231,13 @@ static Transaction * xaccSRGetTrans (SplitRegister *reg,
VirtualCellLocation vcell_loc);
static Split * xaccSRGetCurrentTransSplit (SplitRegister *reg,
VirtualCellLocation *vcell_loc);
static void xaccSRActuallySaveChangedCells (SplitRegister *reg, Transaction *trans,
Split *split, guint32 changed );
static void xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans,
Split *split);
static void xaccSRSaveChangedTemplateCells (SplitRegister *reg,
Transaction *trans,
Split *split);
static gboolean xaccSRFindSplit (SplitRegister *reg,
Transaction *trans, Split *trans_split,
Split *split, CursorClass cursor_class,
@@ -393,7 +399,7 @@ gnc_copy_split_onto_split(Split *from, Split *to, gboolean use_cut_semantics)
}
/* Uses the scheme transaction copying routines */
static void
void
gnc_copy_trans_onto_trans(Transaction *from, Transaction *to,
gboolean use_cut_semantics,
gboolean do_commit)
@@ -1272,7 +1278,11 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir,
info->blank_split_edited = TRUE;
if ( reg->template ) {
xaccSRSaveChangedTemplateCells( reg, trans, blank_split );
} else {
xaccSRSaveChangedCells(reg, trans, blank_split);
}
gnc_resume_gui_refresh ();
@@ -2673,6 +2683,8 @@ xaccSRRedrawReg (SplitRegister *reg)
/* Copy from the register object to scheme. This needs to be
* in sync with xaccSRSaveRegEntry and xaccSRSaveChangedCells. */
// jsled: This will need to be modified, as well.
static gboolean
xaccSRSaveRegEntryToSCM (SplitRegister *reg, SCM trans_scm, SCM split_scm,
gboolean use_cut_semantics)
@@ -2815,7 +2827,9 @@ xaccSRSaveRegEntryToSCM (SplitRegister *reg, SCM trans_scm, SCM split_scm,
/* ======================================================== */
/* Copy from the register object to the engine */
// jsled: okay... the fun.
// actually, not really the fun, but scan this to see if anything
// jumps out; the
gboolean
xaccSRSaveRegEntry (SplitRegister *reg, gboolean do_commit)
{
@@ -2829,6 +2843,8 @@ xaccSRSaveRegEntry (SplitRegister *reg, gboolean do_commit)
const char *memo;
const char *desc;
//DEBUG( "=== In xaccSRSaveRegEntry\n" );
/* get the handle to the current split and transaction */
split = xaccSRGetCurrentSplit (reg);
trans = xaccSRGetCurrentTrans (reg);
@@ -2922,13 +2938,16 @@ xaccSRSaveRegEntry (SplitRegister *reg, gboolean do_commit)
DEBUG ("updating trans addr=%p\n", trans);
if ( reg->template ) {
xaccSRSaveChangedTemplateCells( reg, trans, split );
} else {
xaccSRSaveChangedCells (reg, trans, split);
}
memo = xaccSplitGetMemo (split);
memo = memo ? memo : "(null)";
desc = xaccTransGetDescription (trans);
desc = desc ? desc : "(null)";
PINFO ("finished saving split %s of trans %s \n", memo, desc);
/* If the modified split is the "blank split", then it is now an
@@ -3122,7 +3141,6 @@ sr_split_auto_calc (SplitRegister *reg, Split *split, guint32 changed)
message,
default_value,
radio_list);
for (node = radio_list; node; node = node->next)
g_free (node->data);
@@ -3190,14 +3208,149 @@ sr_split_auto_calc (SplitRegister *reg, Split *split, guint32 changed)
/* ======================================================== */
static void
xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
xaccSRSaveChangedTemplateCells( SplitRegister *reg,
Transaction *trans,
Split *split )
{
SRInfo *info = xaccSRGetInfo( reg );
Split *other_split = NULL;
guint32 changed;
kvp_frame *kvpf;
AccountGroup *template_ag;
Account *template_acc;
kvp_value *tag_val;
DEBUG( "=== In xaccSRSaveChangedTemplateCells\n" );
//template_acc = gncGetTemplateAccount();
//template_ag = gnc_book_get_template_group( gncGetCurrentBook() );
template_acc = reg->templateAcct;
changed = xaccSplitRegisterGetChangeFlag( reg );
changed |= xaccSplitRegisterGetConditionalChangeFlag( reg );
if ( (MOD_DATE & changed) ||
(MOD_NUM & changed) ||
(MOD_RECN & changed) )
{
PERR( "unexpected changed field in a template register: %32x\n", changed );
}
// We'll be using the Split's KVP frame a lot...
kvpf = xaccSplitGetSlots( split );
if ( MOD_XFRM & changed )
{
// FIXME: This should probably do the same checks as
// xaccSRSaveChangedCells regarding account types
// [between different currency accounts, mainly]
char *new_name;
Account *acct;
AccountGroup *acctGrp;
GUID *acctGUID;
// save the account GUID into the kvp_data.
new_name = reg->xfrmCell->cell.value;
acctGrp = gnc_book_get_group( gncGetCurrentBook() );
acct = xaccGetAccountFromFullName( acctGrp, new_name,
gnc_get_account_separator() );
if ( acct == NULL ) {
PERR( "unknown group \"%s\"\n", new_name );
return;
}
acctGUID = (const GUID *)xaccAccountGetGUID( acct );
kvp_frame_set_slot( kvpf, "sched-xaction/xfrm",
kvp_value_new_guid( acctGUID ) );
kvpf = xaccSplitGetSlots( split );
changed ^= MOD_XFRM;
// +DEBUG
if ( 0 ) {
GList *foo = xaccAccountGetSplitList( template_acc );
if ( foo ) {
printf( "Splits:\n" );
do {
printf ("\tsplit: \"%s\"\n", xaccSplitGetMemo( (Split*)foo->data ) );
} while ( (foo = foo->next) );
} else {
printf( "No Splits.\n" );
}
}
// -DEBUG
// set the actual account to the fake account for
// these templates...
xaccAccountInsertSplit (template_acc, split);
// +DEBUG
if ( 0 ) {
GList *foo = xaccAccountGetSplitList( template_acc );
if ( foo ) {
printf( "Splits:\n" );
do {
printf ("\tsplit: \"%s\"\n", xaccSplitGetMemo( (Split*)foo->data ) );
} while ( (foo = foo->next) );
} else {
printf( "No Splits.\n" );
}
}
// -DEBUG
}
if ( MOD_MXFRM & changed )
{
// DTRT
DEBUG( "Template: Got MOD_MXFRM changed\n" );
changed ^= MOD_MXFRM;
}
if ( MOD_AMNT & changed )
{
char *amountStr = "x + y/42";
gnc_numeric new_amount;
gnc_numeric credit;
gnc_numeric debit;
//credit = xaccGetPriceCellValue(reg->creditCell);
//debit = xaccGetPriceCellValue(reg->debitCell);
//new_amount = gnc_numeric_sub_fixed (debit, credit);
// FIXME: the credit/debit cells are limited to
// numeric values by definition [and code]. Blegh.
DEBUG( "kvp_frame before: %s\n", kvp_frame_to_string( kvpf ) );
//amountStr = gnc_numeric_to_string( new_amount );
kvp_frame_set_slot( kvpf, "sched-xaction/credit_formula",
kvp_value_new_string( reg->formCreditCell->cell.value ) );
kvp_frame_set_slot( kvpf, "sched-xaction/debit_formula",
kvp_value_new_string( reg->formDebitCell->cell.value ) );
DEBUG( "kvp_frame after: %s\n", kvp_frame_to_string( kvpf ) );
changed ^= MOD_AMNT;
// set the amount to an innocuous value
xaccSplitSetValue (split, gnc_numeric_create(0, 1) );
}
if ( MOD_SHRS & changed )
{
char *sharesStr = "(x + y)/42";
// FIXME: shares cells are numeric by definition.
DEBUG( "kvp_frame before: %s\n", kvp_frame_to_string( kvpf ) );
//sharesStr = gnc_numeric_to_string( sharesStr );
kvp_frame_set_slot( kvpf, "sched-xaction/shares",
kvp_value_new_string( sharesStr ) );
DEBUG( "kvp_frame after: %s\n", kvp_frame_to_string( kvpf ) );
// set the shares to an innocuous value
xaccSplitSetSharePriceAndAmount (split,
gnc_numeric_create(0, 1),
gnc_numeric_create(0, 1) );
changed ^= MOD_SHRS;
}
xaccSRActuallySaveChangedCells( reg, trans, split, changed );
}
static void
xaccSRActuallySaveChangedCells( SplitRegister *reg, Transaction *trans, Split *split, guint32 changed )
{
SRInfo *info = xaccSRGetInfo (reg);
Split *other_split = NULL;
/* copy the contents from the cursor to the split */
if (MOD_DATE & changed)
@@ -3214,7 +3367,6 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
xaccTransSetDateTS (trans, &ts);
}
if (MOD_NUM & changed)
{
DEBUG ("MOD_NUM: %s\n",
@@ -3273,6 +3425,10 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
* a two-line display, we want to reparent the "other" split, but
* only if there is one. XFRM is the straight split, MXFRM is the
* mirrored split. */
// jsled: this is where it starts to get fun. in the template
// register, we save the XFRM account in the kvp frame.
// also, when loading, we load from the kvp data.
if (MOD_XFRM & changed)
{
Account *old_acc;
@@ -3473,6 +3629,22 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
xaccSplitScrub (other_split);
}
}
}
static void
xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
{
SRInfo *info = xaccSRGetInfo (reg);
Split *other_split = NULL;
guint32 changed;
changed = xaccSplitRegisterGetChangeFlag (reg);
changed |= xaccSplitRegisterGetConditionalChangeFlag (reg);
// all the code in xaccSRActuallySaveChangedCells was right here,
// before. -- jsled
xaccSRActuallySaveChangedCells( reg, trans, split, changed );
}
/* ======================================================== */
@@ -3632,6 +3804,125 @@ use_security_cells (SplitRegister *reg, VirtualLocation virt_loc)
return FALSE;
}
const char *
xaccSRTemplateGetEntryHandler( VirtualLocation virt_loc,
gboolean translate,
gboolean *conditionally_changed,
gpointer user_data )
{
SplitRegister *reg = user_data;
const char *value = "";
CellType cell_type;
Transaction *trans;
Split *split;
kvp_frame *kvpf;
GUID *tmpguid;
//DEBUG( "In xaccSRTemplateGetEntryHandler\n" );
cell_type = xaccSplitRegisterGetCellType (reg, virt_loc);
split = sr_get_split (reg, virt_loc.vcell_loc);
if (split == NULL)
{
return xaccSRGetEntryHandler( virt_loc, translate,
conditionally_changed, user_data );
}
trans = xaccSplitGetParent (split);
kvpf = xaccSplitGetSlots( split );
switch (cell_type) {
case XFRM_CELL:
{
static char *name = NULL;
char account_separator;
if ( kvpf != NULL ) {
DEBUG( "kvp_frame: %s\n", kvp_frame_to_string( kvpf ) );
tmpguid = kvp_value_get_guid( kvp_frame_get_slot( kvpf,
"sched-xaction/xfrm" ) );
DEBUG( "Got the guid \"%s\"\n", guid_to_string( tmpguid ) );
account_separator = gnc_get_account_separator();
DEBUG( "foo\n" );
name = xaccAccountGetFullName (xaccAccountLookup( tmpguid ),
account_separator);
DEBUG( "bar\n" );
DEBUG( "Got the full name: %s\n", name );
} else {
name = "";
}
return name;
}
break;
case CRED_CELL:
case DEBT_CELL:
{
char *amtStr;
gnc_numeric amount;
if ( kvpf != NULL ) {
amtStr = kvp_value_get_string( kvp_frame_get_slot( kvpf,
"sched-xaction/amnt" ) );
amount = gnc_numeric_create( 0, 1 );
string_to_gnc_numeric( amtStr, &amount );
if (gnc_numeric_zero_p (amount))
return "";
if (gnc_numeric_negative_p (amount) && (cell_type == DEBT_CELL))
return "";
if (gnc_numeric_positive_p (amount) && (cell_type == CRED_CELL))
return "";
amount = gnc_numeric_abs (amount);
//return xaccPrintAmount (amount,
//gnc_split_value_print_info (split, FALSE));
// jsled_FIXME: This should be fixed
// to be correct for the "fake" account.
return xaccPrintAmount( amount,
gnc_default_print_info( FALSE ) );
} else {
return "";
}
}
break;
case FCRED_CELL:
{
char *formulaStr;
if ( kvpf != NULL ) {
return kvp_value_get_string( kvp_frame_get_slot( kvpf,
"sched-xaction/credit_formula" ) );
}
}
break;
case FDEBT_CELL:
{
char *formulaStr;
if ( kvpf != NULL ) {
return kvp_value_get_string( kvp_frame_get_slot( kvpf,
"sched-xaction/debit_formula" ) );
}
}
break;
case MXFRM_CELL:
{
return "FIXME:MXFRM";
}
break;
} // end switch
return xaccSRGetEntryHandler( virt_loc,
translate,
conditionally_changed,
user_data );
}
const char *
xaccSRGetEntryHandler (VirtualLocation virt_loc, gboolean translate,
gboolean *conditionally_changed, gpointer user_data)
@@ -3796,8 +4087,6 @@ xaccSRGetEntryHandler (VirtualLocation virt_loc, gboolean translate,
{
static char *name = NULL;
g_free (name);
name = xaccAccountGetFullName (xaccSplitGetAccount (split),
account_separator);
@@ -3868,8 +4157,6 @@ xaccSRGetEntryHandler (VirtualLocation virt_loc, gboolean translate,
Split *s = xaccSplitGetOtherSplit (split);
static char *name = NULL;
g_free (name);
if (s)
name = xaccAccountGetFullName (xaccSplitGetAccount (s),
account_separator);
@@ -4108,6 +4395,11 @@ xaccSRGetLabelHandler (VirtualLocation virt_loc, gpointer user_data)
case NOTES_CELL:
return _("Notes");
case FCRED_CELL:
return _("Credit Formula");
case FDEBT_CELL:
return _("Debit Formula");
case NO_CELL:
return "";
@@ -4120,6 +4412,15 @@ xaccSRGetLabelHandler (VirtualLocation virt_loc, gpointer user_data)
return "";
}
CellIOFlags
xaccSRTemplateGetIOFlagsHandler( VirtualLocation virt_loc,
gpointer user_data )
{
//printf( "In xaccSRTemplateGetIOFlagsHandler\n" );
return xaccSRGetIOFlagsHandler( virt_loc,
user_data );
}
CellIOFlags
xaccSRGetIOFlagsHandler (VirtualLocation virt_loc, gpointer user_data)
{
@@ -4139,6 +4440,8 @@ xaccSRGetIOFlagsHandler (VirtualLocation virt_loc, gpointer user_data)
case MEMO_CELL:
case MXFRM_CELL:
case NOTES_CELL:
case FCRED_CELL:
case FDEBT_CELL:
return XACC_CELL_ALLOW_ALL;
case CRED_CELL:
@@ -4356,7 +4659,9 @@ xaccSRGetBGColorHandler (VirtualLocation virt_loc,
if ((cell_type != DEBT_CELL) &&
(cell_type != CRED_CELL) &&
(cell_type != TDEBT_CELL) &&
(cell_type != TCRED_CELL))
(cell_type != TCRED_CELL) &&
(cell_type != FCRED_CELL) &&
(cell_type != FDEBT_CELL) )
*hatching = FALSE;
else
{
@@ -4439,6 +4744,14 @@ xaccSRGetBGColorHandler (VirtualLocation virt_loc,
}
}
gboolean
xaccSRTemplateConfirmHandler( VirtualLocation virt_loc,
gpointer user_data )
{
//DEBUG( "In xaccSRConfirmHandler\n" );
return xaccSRConfirmHandler( virt_loc, user_data );
}
gboolean
xaccSRConfirmHandler (VirtualLocation virt_loc,
gpointer user_data)

View File

@@ -166,6 +166,8 @@ void xaccSRLoadRegister (SplitRegister *reg, GList * slist,
* a refresh will result in a new blank transaction.
* The method returns TRUE if something was changed. */
gboolean xaccSRSaveRegEntry (SplitRegister *reg, gboolean do_commit);
gboolean xaccSRTemplateSaveRegEntry (SplitRegister *reg,
gboolean do_commit);
/* The xaccSRRedrawReg() method should be called soon
* after the xaccSRSaveRegEntry() method. It checks the
@@ -209,10 +211,16 @@ const char * xaccSRGetEntryHandler (VirtualLocation virt_loc,
gboolean translate,
gboolean *changed,
gpointer user_data);
const char * xaccSRTemplateGetEntryHandler (VirtualLocation virt_loc,
gboolean translate,
gboolean *changed,
gpointer user_data);
const char * xaccSRGetLabelHandler (VirtualLocation virt_loc,
gpointer user_data);
CellIOFlags xaccSRGetIOFlagsHandler (VirtualLocation virt_loc,
gpointer user_data);
CellIOFlags xaccSRTemplateGetIOFlagsHandler (VirtualLocation virt_loc,
gpointer user_data);
guint32 xaccSRGetFGColorHandler (VirtualLocation virt_loc,
gpointer user_data);
guint32 xaccSRGetBGColorHandler (VirtualLocation virt_loc,
@@ -223,5 +231,14 @@ void xaccSRGetCellBorderHandler (VirtualLocation virt_loc,
gpointer user_data);
gboolean xaccSRConfirmHandler (VirtualLocation virt_loc,
gpointer user_data);
gboolean xaccSRTemplateConfirmHandler (VirtualLocation virt_loc,
gpointer user_data);
// jsled-added 2001.05.19 for export to dialog-nextrun.c [which will
// change its name at some point]
void gnc_copy_trans_onto_trans(Transaction *from, Transaction *to,
gboolean use_cut_semantics,
gboolean do_commit);
#endif /* __XACC_SPLIT_LEDGER_H__ */

144
src/doc/TODO-schedxactions Normal file
View File

@@ -0,0 +1,144 @@
Author: jsled@asynchronous.org
Main Scheduled Transaction todo list
------------------------------------
. Engine ...Init() functions should be private.
. FreqSpec.c
. xaccFreqSpecGetFreqStr [display] needs to go away
. finish the GNCFrequency widget
. finish/recreate restoring UI state from FreqSpec.
X add the ...GetFreqStr() code for the complex composite FreqSpecs
X fix the ...GetNextInstance() code
X XMLv2 I/O
X create a template register
X store account and amount data in kvp_frames.
. find a way to list multiple registers for template transaction purposes.
. this is a "normal" general ledger + a "stock" general ledger
. this is going to be: a tabbed notebook in which the tabs contain
the number of transactions the GL they hold contain.
X need policy for start-dates in GNCFrequency
X fix GetNextInstance and GetInstanceAfter, mostly for composite FreqSpecs
X also needs to deal with >28-mday values WRT last-occur date + multipliers
. add a big calendar [gnome-pim? evo?] SXaction List code
. match existing transactions for long-time users
. based off template-transaction data, some big existing-transaction
scanning code.
. need "since-last-run" UI for instanatiation.
. create transactions
X numeric
. formula
. need variable fill-in UI...
. re-use gnome-sheet code for a variable-binding table?
. register mods
. to see upcoming xactions, modifying future balance
. to instantiate xactions.
. to note which are "recurring"
. the mozilla "reload" glyph is kinda neat...
. would like "don't even tell me you instantiated it, just fscking do
it!" option for non-manual-conf SXes
. deal better with formulas in template transactions [real FormulaCell]
. recognize purely numeric template transactions and balance at
template-creation time.
. GNCFrequency
. weekly options
. restoring FreqSpec into GNCFrequency UI
. initial-settings synchronization [start date, optionmenus]
. backend support
. PostgreSQL
. others?
Enhancements:
. SX name should be default value for template transaction description
. loan/repayment SX's
. integrate "Financial Calculator"
. auto-determine number of occurances/end condition from liability
account amount.
========================================
Template Registers/Transactions
Template-option registers are used for users to enter the template
transaction to be created when the scheduled transaction comes due.
Most fields will be copied directly, but certain fields will need to
be modified upon instantiation, and the template version of these
transactions will need to contain the information necessary to do that
instiantion. For some fields, this will be a derivable quantity, and
for others the user will need to be prompted. This will show up
mostly for the amount of variable-amount transactions; perhaps the
amount is then derivable from some external source. These amounts
will then be represented by a variable, a function, or a formula
including both.
Examples...
Verizon bill: "108.83 +- x"
. x: tax amounts [user-input]
Power: "seasonal_util_gas( 'OAK_CA', '100 therms' ) + seasonal_util_elec( 'OAK_CA', '220 kwhrs' )"
. seasonal_util_gas( loc, amt ): regional/seasonal power price
. seasonal_util_elec( loc, amt ): regional/seasonal electricity price
Rent: 1900
Phone: "26"
Internet: "80 - 40 - 10" [3 splits]
Daily: "4 + 6 (+ 26)"
. 4: cigarettes
. 6: avg lunch
. (opt) 20: wine
Gas: "regional_auto_gas( 'BERK_CA', 'premium', 14 gal +- 2 )"
. regional_gas( loc, type, formula )
. loc: location
. type: subclass of gasoline
. formula: formula to use for user-query
TROA: "220.14"
User-input
When user input is required for scheduled transaction instantiation,
we should have some sort of simple, spreadsheet-like UI for the entry
and verification of values. This would allow the entry of all values
necessary for the formula to be calculated.
We also need a way to defer instantiation of a scheduled transaction
until the information can be obtained.
Example...
type | date | formula | variable | value | total
+---------------------------------------------------------------------------+
| PG&E | 2001.04.12 | lookup(x) + lookup| | | 184 |
|---------------------------------------------------------------------------|
| | | | x | 142 | |
|---------------------------------------------------------------------------|
| | | | y | 42 | |
|---------------------------------------------------------------------------|
| gas | 2001.03.01 | cost/gal * gal | | | |
|---------------------------------------------------------------------------|
| | | | cost/gal | 1.949 | 14.27 |
|---------------------------------------------------------------------------|
| | | | gal | 12.32 | |
|---------------------------------------------------------------------------|
| gas | 2001.03.12 | cost/gal * gal | | | |
|---------------------------------------------------------------------------|
| | | | cost/gal | 1.959 | 15.39 |
|---------------------------------------------------------------------------|
| | | | gal | 13.43 | |
+---------------------------------------------------------------------------+
I believe this means we can use the existing gnc-sheet/register-style
thingy.
We probably want to defer the lookup-based stuff until
1.8/2.0/gnumatic-provided network backing for this.
Stocks/recurring share purchases can make use of this, as well. The
idea is then, probably, that the user will have a fixed amount with
which to buy as many shares as possible; the num-shares, then, is a
formula based on a lookup at the given time [the share price],
involving the function FLOOR.
It'd be nice if this formula could be either a simple infix syntax for
normal people, or a sexp for Schemers.

812
src/engine/FreqSpec.c Normal file
View File

@@ -0,0 +1,812 @@
/********************************************************************\
* FreqSpec.c -- Frequency specifier implementation. *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* Copyright (C) 2001 Ben Stanley <bds02@uow.edu.au> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
/********************************************************************\
Current status
All kinds of repeats work, including composites. This is tested -
although composites need some more test cases to be put into the test
suite - ../test/test-freq-spec.c
FreqSpec objects are currently 'set' to give them the information
they need. Separate methods for modifying currently existing FreqSpec
objects are not provided. In the case of composites, you may add FreqSpec
objects to a composite, and you may access a list of the FreqSpec objects
which form the composite. This interface allows you to do damage...
TODO list
Ben Stanley 2001-04-02
* Write xaccFreqSpecGetFreqStr (I wonder how this will be
internationalised... I suspect that this code will need to be
re-written for each language, because the code will have to
generate grammar... It's more than just translating strings.)
However, the first priority is to write one that works for
English.
* Write a function to allow you to query whether a given
date forms part of the recurrence.
* Write a method to get the previous recurrence
* provide XML Load/Save functionality for this object.
* Figure out xaccFreqSpecIsValidDate - I suspect that this is the
'query whether a given date forms part of the recurrence'
above.
* FIGURE OUT WHAT'S GOING ON WITH xaccFreqSpecGetUIType AND
xaccFreqSpecSetUIType.
* Try to reduce the size of the data structure. There are quite a few
32 bit fields which could be stored in about 8 bits.
* Add public methods to allow for recurrences with an interval
of 1 to be set without reference to an initial 'date' - monthly
things in particular. Try to reduce the dependence on an initial
date for the input to set up the recurrence.
Questions:
Is it best that the public interface stay as GDate, or should it
really be a timespec? I have no problem with converting GDates to
timespecs for load/save if that makes life easier.
However, I chose to use GDate internally because I have used a *lot*
of the date calculating ability of GDate in the internal implementation.
GDate has simplified this work enormously compared to using struct tm
and time_t. The engine's timespec object doesn't appear to have the
required functionality either, so I would need to write the required
functions for timespec (perhaps by implementing in terms of GDate?).
Hopefully it's not too painful to leave GDate in the public interface
and change other code to use it.
\********************************************************************/
#include "config.h"
/* #include <time.h> // should be config'd */
#include <glib.h>
#include <string.h>
#include "FreqSpecP.h"
#include "GNCIdP.h"
/*#include "Transaction.h"*/
/*#include "TransactionP.h"*/
#include "date.h"
#include "gnc-engine-util.h"
#include "gnc-event-p.h"
#include "messages.h"
/* I have done this to prevent compiler warnings...
* This is used to convert a const GDate* to a GDate* for passing
* to the glib g_date_xxx functions which don't use const...
* Strangely, most of the rest of glib does use const, so
* perhaps this will change? When it does, just define this macro to
* nothing and the compiler will check the constness of each pointer....
*/
#define CONST_HACK (GDate*)
static short module = MOD_SX;
/** PROTOTYPES ******************************************************/
/**
* Destroys all sub-FreqSpecs in a composite FreqSpec.
* Assertion error if it's not a COMPOSITE FreqSpec.
**/
void xaccFreqSpecCompositesClear( FreqSpec *fs );
void subSpecsListMapDelete( gpointer data, gpointer user_data );
/** Local data defs *****/
/**
* The number of days in each month.
**/
struct monthDesc {
char *dshort;
char *dlong;
gint numDays;
};
/* This stuff is going to need i18n.
* wouldn't it be simpler to use the system
* date conversion functions?
* glib already knows about this. */
static char *weekDayNames[] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
/* hmmm... glib already knows all about this. */
struct monthDesc monthInfo[] = {
#define M_JAN 0
{ "Jan", "January", 31 },
#define M_FEB 1
{ "Feb", "February", 28 },
#define M_MAR 2
{ "Mar", "March", 31 },
#define M_APR 3
{ "Apr", "April", 30 },
#define M_MAY 4
{ "May", "May", 31 },
#define M_JUN 5
{ "Jun", "June", 30 },
#define M_JUL 6
{ "Jul", "July", 31 },
#define M_AUG 7
{ "Aug", "August", 31 },
#define M_SEP 8
{ "Sep", "September", 30 },
#define M_OCT 9
{ "Oct", "October", 31 },
#define M_NOV 10
{ "Nov", "November", 30 },
#define M_DEC 11
{ "Dec", "December", 31 }
};
/** Local Prototypes *****/
FreqSpec*
xaccFreqSpecMalloc()
{
FreqSpec *fs = g_new0(FreqSpec, 1);
xaccFreqSpecInit( fs );
// FIXME:event
gnc_engine_generate_event( &fs->guid, GNC_EVENT_CREATE );
return fs;
}
void
xaccFreqSpecInit( FreqSpec *fs )
{
g_return_if_fail( fs );
xaccGUIDNew( &fs->guid );
xaccStoreEntity( fs, &fs->guid, GNC_ID_FREQSPEC );
fs->type = INVALID;
fs->uift = UIFREQ_ONCE;
memset( &(fs->s), 0, sizeof(fs->s) );
}
void
xaccFreqSpecCleanUp( FreqSpec *fs )
{
g_return_if_fail( fs );
switch ( fs->type ) {
case INVALID:
case ONCE:
case DAILY:
case WEEKLY:
case MONTHLY:
case MONTH_RELATIVE:
break;
case COMPOSITE:
xaccFreqSpecCompositesClear( fs );
g_list_free( fs->s.composites.subSpecs );
break;
default:
g_return_if_fail(FALSE);
}
fs->type = INVALID;
}
void
xaccFreqSpecFree( FreqSpec *fs )
{
if ( fs == NULL ) return;
gnc_engine_generate_event( &fs->guid, GNC_EVENT_DESTROY );
xaccRemoveEntity( &fs->guid );
xaccFreqSpecCleanUp( fs );
g_free( fs );
}
FreqType
xaccFreqSpecGetType( FreqSpec *fs )
{
g_return_val_if_fail( fs, INVALID );
/* Is this really a fail? */
g_return_val_if_fail( fs->type != INVALID, INVALID );
return fs->type;
}
UIFreqType
xaccFreqSpecGetUIType( FreqSpec *fs )
{
g_return_val_if_fail( fs, INVALID );
return fs->uift;
}
void
xaccFreqSpecSetUIType( FreqSpec *fs, UIFreqType newUIFreqType )
{
g_return_if_fail( fs );
fs->uift = newUIFreqType;
}
/*
void
xaccFreqSpecSetTypes( FreqSpec *fs, FreqType newFT, UIFreqType newUIFT )
{
g_return_if_fail( fs );
xaccFreqSpecSetType( fs, newFT );
xaccFreqSpecSetUIType( fs, newUIFT );
}
*/
static inline guint32 min( guint32 a, guint32 b )
{
return a > b ? b : a;
}
void
xaccFreqSpecGetNextInstance(
FreqSpec *fs,
const GDate* in_date,
GDate* out_date )
{
GList *list, *blist;
int mon;
g_return_if_fail( fs );
switch( fs->type ) {
case INVALID:
g_return_if_fail(FALSE);
case ONCE:
if ( g_date_compare( &(fs->s.once.date), CONST_HACK in_date ) > 0 ) {
*out_date = fs->s.once.date;
} else {
/* Date is past due. Return an invalid date. */
g_date_clear( out_date, 1 );
}
break;
case DAILY: {
guint32 julian_in_date, julian_next_repeat, complete_intervals;
julian_in_date = g_date_julian( CONST_HACK in_date );
complete_intervals =
(julian_in_date - fs->s.daily.offset_from_epoch) /
fs->s.daily.interval_days;
julian_next_repeat =
fs->s.daily.offset_from_epoch +
(complete_intervals + 1) * fs->s.daily.interval_days;
g_date_set_julian( out_date, julian_next_repeat );
} break;
case WEEKLY: {
/* This implementation stores the offset from epoch as the number
* of days, not week epoch offset and day in week offset.
* It is very similar to the daily repeat representation. */
guint32 julian_in_date, julian_next_repeat, complete_intervals;
julian_in_date = g_date_julian( CONST_HACK in_date );
complete_intervals =
(julian_in_date - fs->s.weekly.offset_from_epoch) /
(fs->s.weekly.interval_weeks * 7);
julian_next_repeat =
fs->s.weekly.offset_from_epoch +
(complete_intervals + 1) * fs->s.weekly.interval_weeks * 7;
g_date_set_julian( out_date, julian_next_repeat );
/* This code passes the test, but it seems large and complicated...
* it uses a separate week offset from epoch and day in week offset. */
/* guint32 julian_in_date, julian_next_repeat, complete_intervals,
in_weeks_from_epoch, after_repeat_in_week_interval;
julian_in_date = g_date_julian( CONST_HACK in_date );
in_weeks_from_epoch = (julian_in_date-1) / 7;
complete_intervals =
(in_weeks_from_epoch -
fs->s.weekly.offset_from_epoch) /
fs->s.weekly.interval_weeks;
after_repeat_in_week_interval =
((julian_in_date-1) % 7 >= fs->s.weekly.day_of_week ||
(in_weeks_from_epoch - fs->s.weekly.offset_from_epoch) %
fs->s.weekly.interval_weeks > 0 ) ? 1 : 0;
julian_next_repeat =
(fs->s.weekly.offset_from_epoch +
(complete_intervals + after_repeat_in_week_interval) *
fs->s.weekly.interval_weeks) * 7 +
fs->s.weekly.day_of_week + 1;
g_date_set_julian( out_date, julian_next_repeat );
*/
} break;
case MONTHLY: {
guint32 in_months_from_epoch, after_repeat_in_month_interval,
complete_intervals, next_repeat_months_from_epoch, month, year;
in_months_from_epoch = (g_date_year( CONST_HACK in_date )-1) * 12 +
g_date_month( CONST_HACK in_date ) - 1;
complete_intervals =
(in_months_from_epoch - fs->s.monthly.offset_from_epoch) /
fs->s.monthly.interval_months;
after_repeat_in_month_interval =
(g_date_day( CONST_HACK in_date ) >= fs->s.monthly.day_of_month ||
(in_months_from_epoch - fs->s.monthly.offset_from_epoch) %
fs->s.monthly.interval_months > 0 ||
g_date_day( CONST_HACK in_date ) >=
g_date_days_in_month( g_date_month( CONST_HACK in_date ),
g_date_year( CONST_HACK in_date ) ) ) ? 1 : 0;
next_repeat_months_from_epoch =
fs->s.monthly.offset_from_epoch +
(complete_intervals + after_repeat_in_month_interval) *
fs->s.monthly.interval_months;
/* Hmmm... what happens if the day of the month is greater than the
* number of days in this month?
* Here I have constrained the day of the month by the number
* of days in the month. This is compensated for above by checking if
* the input day is the last day of that month, in which case it will
* move to the next month interval.
*/
month = next_repeat_months_from_epoch % 12 + 1;
year = next_repeat_months_from_epoch / 12 + 1;
g_date_set_dmy( out_date,
min( fs->s.monthly.day_of_month,
g_date_days_in_month( month, year ) ),
month,
year );
} break;
case MONTH_RELATIVE: {
guint32 in_months_from_epoch, after_repeat_in_month_interval,
complete_intervals, next_repeat_months_from_epoch, month, year,
wday_of_1st, day_of_repeat;
GDate date1;
in_months_from_epoch = (g_date_year( CONST_HACK in_date )-1) * 12 +
g_date_month( CONST_HACK in_date ) - 1;
complete_intervals =
(in_months_from_epoch - fs->s.month_relative.offset_from_epoch) /
fs->s.month_relative.interval_months;
month = g_date_month( CONST_HACK in_date );
year = g_date_year( CONST_HACK in_date );
g_date_set_dmy( &date1, 1, month, year );
wday_of_1st = g_date_weekday( &date1 );
day_of_repeat = (fs->s.month_relative.occurrence-1)*7 +
((fs->s.month_relative.weekday + 7 - wday_of_1st)%7 + 1);
after_repeat_in_month_interval =
(g_date_day( CONST_HACK in_date ) >= day_of_repeat ||
day_of_repeat > g_date_days_in_month( month, year ) ||
(in_months_from_epoch - fs->s.month_relative.offset_from_epoch) %
fs->s.month_relative.interval_months > 0 ) ? 1 : 0;
next_repeat_months_from_epoch =
fs->s.month_relative.offset_from_epoch +
(complete_intervals + after_repeat_in_month_interval) *
fs->s.month_relative.interval_months;
month = next_repeat_months_from_epoch % 12 + 1;
year = next_repeat_months_from_epoch / 12 + 1;
g_date_set_dmy( &date1, 1, month, year );
wday_of_1st = g_date_weekday( &date1 );
/* This calculates the day of the month in the month which forms
* the next month in the cycle after the given input date.
* However, this day may be larger than the number of days in that month... */
day_of_repeat = (fs->s.month_relative.occurrence-1)*7 +
((fs->s.month_relative.weekday + 7 - wday_of_1st)%7 + 1);
while( day_of_repeat > g_date_days_in_month( month, year ) ) {
/* If the repeat occurs after the end of the month, then
* find the next month containing a day which satisfies the request.
* Each candiate month separated by interval_months is considered
* by this loop.*/
++complete_intervals;
next_repeat_months_from_epoch =
fs->s.month_relative.offset_from_epoch +
complete_intervals * fs->s.month_relative.interval_months;
month = next_repeat_months_from_epoch % 12 + 1;
year = next_repeat_months_from_epoch / 12 + 1;
g_date_set_dmy( &date1, 1, month, year );
wday_of_1st = g_date_weekday( &date1 );
day_of_repeat = (fs->s.month_relative.occurrence-1)*7 +
((fs->s.month_relative.weekday + 7 - wday_of_1st)%7 + 1);
/* Hmmm... It would be nice to know that this loop is
* guaranteed to terminate... CHECK ME! */
}
g_date_set_dmy( out_date, day_of_repeat, month, year );
} break;
case COMPOSITE:
list = fs->s.composites.subSpecs;
if ( !list ) {
/* sets date to be invalid */
g_date_clear( out_date, 1 );
break;
}
{
/* This implements || composites. */
guint32 min_julian = 0xFFFFFFFF; /* the biggest unsigned 32 bit number */
guint32 this_julian;
do {
GDate next_repeat;
xaccFreqSpecGetNextInstance(
(FreqSpec*) list->data,
in_date,
&next_repeat );
this_julian = g_date_julian( &next_repeat );
min_julian = min( min_julian, this_julian );
} while ( (list = g_list_next(list)) );
g_date_set_julian( out_date, min_julian );
}
break;
default:
g_date_clear( out_date, 1 );
g_return_if_fail(FALSE);
}
}
/*
char*
xaccFreqSpecIsValidDateRelaxed( FreqSpec *fs, time_t query )
{
return "FIXME: not implemented yet!";
}
*/
void
xaccFreqSpecSetOnceDate( FreqSpec *fs, const GDate* when )
{
g_return_if_fail( fs );
g_return_if_fail( when );
xaccFreqSpecCleanUp( fs );
fs->type = ONCE;
fs->s.once.date = *when;
}
void
xaccFreqSpecSetDaily( FreqSpec *fs,
const GDate* initial_date,
guint interval_days )
{
guint32 julian_days_since_epoch;
g_return_if_fail( fs );
g_return_if_fail( interval_days > 0 );
xaccFreqSpecCleanUp( fs );
fs->type = DAILY;
fs->s.daily.interval_days = interval_days;
julian_days_since_epoch = g_date_julian( CONST_HACK initial_date );
fs->s.daily.offset_from_epoch = julian_days_since_epoch % interval_days;
}
void
xaccFreqSpecSetWeekly( FreqSpec *fs,
const GDate* initial_date,
guint interval_weeks )
{
/* pick one... make sure that the code in next matches this,
* and that the fields in the
* weekly struct match too.
*/
#if 0
/* *
* This implements weekly by using the fact that 1 week = 7 days.
* Weeks start at epoch in this representation, not necesarily Monday,
* so there is not really any difference...
* The weekly tests pass.
*/
guint32 julian_days_since_epoch;
g_return_if_fail( fs );
g_return_if_fail( interval_weeks > 0 );
xaccFreqSpecCleanUp( fs );
fs->type = DAILY;
fs->s.daily.interval_days = 7 * interval_weeks;
julian_days_since_epoch = g_date_julian( CONST_HACK initial_date );
fs->s.daily.offset_from_epoch = julian_days_since_epoch % (7*interval_weeks);
#endif
#if 1
/* simplest solution */
guint32 julian_days_since_epoch;
g_return_if_fail( fs );
g_return_if_fail( interval_weeks > 0 );
xaccFreqSpecCleanUp( fs );
fs->type = WEEKLY;
fs->s.weekly.interval_weeks = interval_weeks;
julian_days_since_epoch = g_date_julian( CONST_HACK initial_date );
fs->s.weekly.offset_from_epoch = julian_days_since_epoch % (7*interval_weeks);
#endif
#if 0
/**
* Use the weekly implementation, which seems to be more complicated...
* uses separate weekly and day in week offsets.
* works.
*/
guint32 julian_day_initial, weeks_since_epoch;
g_return_if_fail( fs );
g_return_if_fail( interval_weeks > 0 );
xaccFreqSpecCleanUp( fs );
fs->type = WEEKLY;
fs->s.weekly.interval_weeks = interval_weeks;
julian_day_initial = g_date_julian( CONST_HACK initial_date );
weeks_since_epoch = (julian_day_initial-1) / 7;
fs->s.weekly.day_of_week = (julian_day_initial-1) % 7;
fs->s.weekly.offset_from_epoch = weeks_since_epoch % interval_weeks;
g_return_if_fail( 0 <= fs->s.weekly.day_of_week );
g_return_if_fail( fs->s.weekly.day_of_week < 7 );
g_return_if_fail( fs->s.weekly.offset_from_epoch < interval_weeks );
g_return_if_fail( 0 <= fs->s.weekly.offset_from_epoch );
#endif
}
void
xaccFreqSpecSetMonthly( FreqSpec *fs,
const GDate* initial_date,
guint interval_months )
{
guint months_since_epoch;
g_return_if_fail( fs );
g_return_if_fail( interval_months > 0 );
xaccFreqSpecCleanUp( fs );
fs->type = MONTHLY;
fs->s.monthly.interval_months = interval_months;
months_since_epoch = (g_date_year( CONST_HACK initial_date )-1) * 12 +
g_date_month( CONST_HACK initial_date ) - 1;
fs->s.monthly.offset_from_epoch = months_since_epoch % interval_months;
fs->s.monthly.day_of_month = g_date_day( CONST_HACK initial_date );
g_return_if_fail( fs->s.monthly.offset_from_epoch <
fs->s.monthly.interval_months );
}
void
xaccFreqSpecSetMonthRelative( FreqSpec *fs,
const GDate* initial_date,
guint interval_months )
{
guint months_since_epoch;
g_return_if_fail( fs );
g_return_if_fail( interval_months > 0 );
xaccFreqSpecCleanUp( fs );
fs->type = MONTH_RELATIVE;
fs->s.month_relative.interval_months = interval_months;
months_since_epoch = (g_date_year( CONST_HACK initial_date )-1) * 12 +
g_date_month( CONST_HACK initial_date ) - 1;
fs->s.month_relative.offset_from_epoch = months_since_epoch % interval_months;
fs->s.month_relative.weekday = g_date_weekday( CONST_HACK initial_date );
fs->s.month_relative.occurrence = (g_date_day( CONST_HACK initial_date )-1) / 7 + 1;
g_return_if_fail( fs->s.month_relative.weekday > 0 );
g_return_if_fail( fs->s.month_relative.weekday <= 7 );
g_return_if_fail( fs->s.month_relative.occurrence > 0 );
g_return_if_fail( fs->s.month_relative.occurrence <= 5 );
g_return_if_fail( fs->s.month_relative.offset_from_epoch <
fs->s.month_relative.interval_months );
}
void
xaccFreqSpecSetComposite( FreqSpec *fs )
{
g_return_if_fail( fs );
xaccFreqSpecCleanUp( fs );
fs->type = COMPOSITE;
fs->s.composites.subSpecs = NULL;
}
GList*
xaccFreqSpecCompositeGet( FreqSpec *fs )
{
g_return_val_if_fail( fs, NULL );
g_return_val_if_fail( fs->type == COMPOSITE, NULL );
return fs->s.composites.subSpecs;
}
void
xaccFreqSpecCompositeAdd( FreqSpec *fs, FreqSpec *fsToAdd )
{
g_return_if_fail( fs );
g_return_if_fail( fs->type == COMPOSITE );
fs->s.composites.subSpecs =
g_list_append( fs->s.composites.subSpecs, fsToAdd );
}
void
subSpecsListMapDelete( gpointer data, gpointer user_data )
{
xaccFreqSpecFree( (FreqSpec*)data );
}
void
xaccFreqSpecCompositesClear( FreqSpec *fs )
{
g_return_if_fail( fs->type == COMPOSITE );
g_list_foreach( fs->s.composites.subSpecs,
subSpecsListMapDelete, NULL );
}
void
xaccFreqSpecGetFreqStr( FreqSpec *fs, GString *str )
{
GList *list;
FreqSpec *tmpFS;
int tmpInt;
char *tmpStr;
int i;
// FIXME: fill in.
switch( xaccFreqSpecGetUIType( fs ) ) {
case UIFREQ_ONCE:
tmpStr = g_new0( char, 25 );
// this is now a GDate.
g_date_strftime( tmpStr, 25,
"%a, %b %e, %Y",
&fs->s.once.date );
g_string_sprintf( str, "Once: %s", tmpStr );
g_free( tmpStr );
break;
case UIFREQ_DAILY:
g_string_sprintf( str, "Daily" );
if ( fs->s.daily.interval_days > 1 ) {
g_string_sprintfa( str, " (x%u)",
fs->s.daily.interval_days );
}
break;
case UIFREQ_DAILY_MF:
g_string_sprintf( str, "Daily [M-F]" );
if ( fs->s.weekly.interval_weeks > 1 ) {
g_string_sprintfa( str, " (x%u)",
fs->s.weekly.interval_weeks );
}
break;
case UIFREQ_WEEKLY:
g_string_sprintf( str, "Weekly" );
tmpInt = -1;
tmpStr = g_new0( char, 8 );
for ( i=0; i<7; i++ ) {
tmpStr[i] = '-';
}
list = xaccFreqSpecCompositeGet( fs );
do {
tmpFS = (FreqSpec*)list->data;
if ( xaccFreqSpecGetType(tmpFS) != WEEKLY ) {
g_string_sprintf( str,
"error: UIFREQ_WEEKLY doesn't contain weekly children" );
return;
}
if ( tmpInt == -1 ) {
tmpInt = tmpFS->s.weekly.interval_weeks;
}
// put the first letter of the weekday name in
// the appropriate position.
// FIXME: need the offset from the day-of-week
// of the Julian epoch
/*
tmpStr[tmpFS->specData.dateAnchor[1]] =
weekDayNames[tmpFS->specData.dateAnchor[1]][0];
*/
} while ( (list = g_list_next(list)) );
if ( tmpInt > 1 ) {
g_string_sprintfa( str, " (x%d)", tmpInt );
}
g_string_sprintfa( str, ": %s", tmpStr );
g_free( tmpStr );
break;
case UIFREQ_SEMI_MONTHLY:
g_string_sprintf( str, "Semi-Monthly" );
list = xaccFreqSpecCompositeGet( fs );
tmpFS = (FreqSpec*)(g_list_nth( list, 0 )->data);
if ( tmpFS->s.monthly.interval_months > 1 ) {
g_string_sprintfa( str, " (x%u)", tmpFS->s.monthly.interval_months );
}
g_string_sprintfa( str, ": " );
if ( tmpFS->s.monthly.day_of_month > 31 ) {
g_string_sprintfa( str, "%s", "last day" );
} else {
g_string_sprintfa( str, "%u", tmpFS->s.monthly.day_of_month );
}
g_string_sprintfa( str, ", " );
tmpFS = (FreqSpec*)(g_list_nth( list, 1 )->data);
if ( tmpFS->s.monthly.day_of_month > 31 ) {
g_string_sprintfa( str, "%s", "last day" );
} else {
g_string_sprintfa( str, "%u", tmpFS->s.monthly.day_of_month );
}
break;
case UIFREQ_MONTHLY:
g_string_sprintf( str, "Monthly" );
if ( fs->s.monthly.interval_months > 1 ) {
g_string_sprintfa( str, " (x%u)",
fs->s.monthly.interval_months );
}
g_string_sprintfa( str, ": %u",
fs->s.monthly.day_of_month );
break;
case UIFREQ_QUARTERLY:
g_string_sprintf( str, "Quarterly" );
if ( fs->s.monthly.interval_months != 3 ) {
g_string_sprintfa( str, " (x%u)",
fs->s.monthly.interval_months/3 );
}
g_string_sprintfa( str, ": %u",
fs->s.monthly.day_of_month );
break;
case UIFREQ_TRI_ANUALLY:
g_string_sprintf( str, "Tri-Anually" );
if ( fs->s.monthly.interval_months != 4 ) {
g_string_sprintfa( str, " (x%u)",
fs->s.monthly.interval_months/4 );
}
g_string_sprintfa( str, ": %u",
fs->s.monthly.day_of_month );
break;
case UIFREQ_SEMI_YEARLY:
g_string_sprintf( str, "Semi-Yearly" );
if ( fs->s.monthly.interval_months != 6 ) {
if ( (fs->s.monthly.interval_months % 6) != 0 ) {
// FIXME:error
printf( "ERROR: FreqSpec Semi-Yearly month-interval "
"is not a multiple of 6 [%d]",
fs->s.monthly.interval_months );
}
g_string_sprintfa( str, " (x%u)",
fs->s.monthly.interval_months/6 );
}
g_string_sprintfa( str, ": %u",
fs->s.monthly.day_of_month );
break;
case UIFREQ_YEARLY:
g_string_sprintf( str, "Yearly" );
if ( fs->s.monthly.interval_months != 12 ) {
if ( (fs->s.monthly.interval_months % 12) != 0 ) {
// FIXME:error
printf( "ERROR: \"Yearly\" FreqSpec month-interval "
"is not a multiple of 12 [%d]",
fs->s.monthly.interval_months );
}
g_string_sprintfa( str, " (x%u)",
fs->s.monthly.interval_months/12 );
}
g_string_sprintfa( str, ": %s/%u",
// FIXME: need the year-of-month value.
monthInfo[/*fs->specData.dateAnchor[2]*/ 0].dshort,
fs->s.monthly.day_of_month );
break;
default:
g_string_sprintf( str, "Unknown" );
break;
}
}

242
src/engine/FreqSpec.h Normal file
View File

@@ -0,0 +1,242 @@
/********************************************************************\
* FreqSpec.h -- Frequency Specification *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* Copyright (C) 2001 Ben Stanley <bds02@uow.edu.au> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#ifndef XACC_FREQSPEC_H
#define XACC_FREQSPEC_H
#include "config.h"
#include <glib.h>
#include "GNCId.h"
/**
* Frequency specification.
*
**/
typedef enum gncp_FreqType {
INVALID,
ONCE,
DAILY,
WEEKLY, /* Hmmm... This is sort of DAILY[7]... */
// BI_WEEKLY: weekly[2]
// SEMI_MONTHLY: use composite
MONTHLY,
MONTH_RELATIVE,
// YEARLY: monthly[12]
COMPOSITE,
} FreqType;
/**
* The user's conception of the frequency. It is expected that this
* list will grow, while the former [FreqType] will not.
*
* Ideally this is not here, but what can you do?
**/
typedef enum gncp_UIFreqType {
UIFREQ_NONE,
UIFREQ_ONCE,
UIFREQ_DAILY,
UIFREQ_DAILY_MF,
UIFREQ_WEEKLY,
UIFREQ_BI_WEEKLY,
UIFREQ_SEMI_MONTHLY,
UIFREQ_MONTHLY,
UIFREQ_QUARTERLY,
UIFREQ_TRI_ANUALLY,
UIFREQ_SEMI_YEARLY,
UIFREQ_YEARLY,
UIFREQ_NUM_UI_FREQSPECS
} UIFreqType;
/**
* Forward declaration of FreqSpec type for storing
* date repetition information. This is an opaque type.
*/
struct gncp_freq_spec;
typedef struct gncp_freq_spec FreqSpec;
/** PROTOTYPES ******************************************************/
/**
* Allocates memory for a FreqSpec.
* Calls xaccFreqSpecInit() to initialise.
**/
FreqSpec* xaccFreqSpecMalloc();
/**
* Initializes a FreqSpec by setting it's to type INVALID.
* Use this to initialise a stack object.
* FreqSpec objects must be initalised before being used by
* any other method.
**/
void xaccFreqSpecInit( FreqSpec *fs );
/**
* destroys any private data belonging to the FreqSpec.
* Use this for a stack object.
*/
void xaccFreqSpecCleanUp( FreqSpec *fs );
/**
* Frees a heap allocated FreqSpec.
* This is the opposite of xaccFreqSpecMalloc().
**/
void xaccFreqSpecFree( FreqSpec *fs );
/**
* Gets the type of a FreqSpec.
**/
FreqType xaccFreqSpecGetType( FreqSpec *fs );
/**
* Sets the type of a FreqSpec.
* Setting the type re-initializes any spec-data; this means
* destroying any sub-types in the case of COMPOSITE.
* THESE FUNCTIONS HAVE NOT BEEN MAINTAINED THROUGH BEN'S CHANGES.
* They need to be checked.
**/
/* void xaccFreqSpecSetType( FreqSpec *fs, FreqType newType ); */
UIFreqType xaccFreqSpecGetUIType( FreqSpec *fs );
void xaccFreqSpecSetUIType( FreqSpec *fs, UIFreqType newUIFreqType );
/* Convenience function; calls the two above. */
/* void xaccFreqSpecSetTypes( FreqSpec *fs, FreqType newType, UIFreqType newUIType ); */
/**
* Sets the type to once-off, and initialises the
* date it occurs on.
* Disposes of any previous data.
*/
void xaccFreqSpecSetOnceDate( FreqSpec *fs,
const GDate* when );
/**
* Sets the type to DAILY. Disposes of any previous data.
* Uses the start date to figure
* out how many days after epoch (1/1/1900) this repeat would
* have first occurred on if it had been running back then.
* This is used later to figure out absolute repeat dates.
*/
void xaccFreqSpecSetDaily( FreqSpec *fs,
const GDate* initial_date,
guint interval_days );
/**
* Sets the type to WEEKLY. Disposes of any previous data.
* Uses the inital date to figure out the day of the week to use.
*/
void xaccFreqSpecSetWeekly( FreqSpec *fs,
const GDate* inital_date,
guint interval_weeks );
/**
* Sets the type to MONTHLY. Disposes of any previous data.
* Uses the inital date to figure out the day of the month to use.
*/
void xaccFreqSpecSetMonthly( FreqSpec *fs,
const GDate* inital_date,
guint interval_months );
/**
* Sets the type to MONTH_RELATIVE. Disposes of any previous data.
* Uses the inital date to figure out the day of the month to use.
*/
void xaccFreqSpecSetMonthRelative( FreqSpec *fs,
const GDate* inital_date,
guint interval_months );
/**
* Sets the type to COMPOSITE. Disposes of any previous data.
* You must Add some repeats to the composite before using
* it for repeating.
*/
void xaccFreqSpecSetComposite( FreqSpec *fs );
/**
* Returns a human-readable string of the Frequency. This uses
* UIFreqType to unroll the internal structure. It is an assertion
* failure if the FreqSpec data doesn't match the UIFreqType.
* Caller allocates the GString [natch].
**/
void xaccFreqSpecGetFreqStr( FreqSpec *fs, GString *str );
/**
* Returns the list of FreqSpecs in a COMPOSITE FreqSpec.
* It is an error to use this if the type is not COMPOSITE.
* The caller should not modify this list;
* only add/remove composites and use this fn to get
* the perhaps-changed list head.
**/
GList* xaccFreqSpecCompositeGet( FreqSpec *fs );
/**
* Adds a FreqSpec to the list in a COMPOSITE FreqSpec; if the
* FreqSpec is not COMPOSITE, this is an assertion failure.
**/
void xaccFreqSpecCompositeAdd( FreqSpec *fs, FreqSpec *fsToAdd );
/**
* Returns the next date which the FreqSpec specifies given the
* previous occurance. Like the relaxed validity check, this doesn't
* take multipliers into account; it just returns the next possible
* occurance after the given day.
* bstanley I think this should be private.
**/
/*
time_t xaccFreqSpecGetInstanceAfter( FreqSpec *fs, time_t after );
*/
/**
* Returns the next instance of the FreqSpec after a given input date.
* Note that if the given date happens to be a repeat date,
* then the next repeat date will be returned.
**/
void xaccFreqSpecGetNextInstance( FreqSpec *fs,
const GDate* in_date,
GDate* out_date );
/**
* Returns either NULL [valid] or a descriptive string [reason not
* valid] for the given query date. This is "relaxed", in that it
* doesn't care about the frequency multiplier [e.g., For
* WEEKLY[2]:Wed, all Wednesdays are valid; for MONTHLY[12]:16, the
* 16th day of every month is valid.
**/
/*
char* xaccFreqSpecIsValidDateRelaxed( FreqSpec *fs, time_t query );
*/
/**
* A strict validity check. Given a previous and query date, returns
* NULL if the query date is valid, or a text reason if not.
**/
/*
char* xaccFreqSpecIsValidDate( FreqSpec *fs, time_t previous, time_t query );
*/
#endif /* XACC_FREQSPEC_H */

128
src/engine/FreqSpecP.h Normal file
View File

@@ -0,0 +1,128 @@
/********************************************************************\
* FreqSpec.h -- Frequency Specification *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* Copyright (C) 2001 Ben Stanley <bds02@uow.edu.au> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
/********************************************************************\
This file contains private definitions and should not be used by
other parts of the engine. This is private data and is subject to
change.
Currently the only files which include this file are:
FreqSpec.c
gnc-freqspec-xml-v2.c
\********************************************************************/
#ifndef XACC_FREQSPECP_H
#define XACC_FREQSPECP_H
#include "FreqSpec.h"
/**
* Scheduled transactions have a frequency defined by a frequency
* specifier. This specifier, given a start date, end date [present
* in the scheduled transaction] and last occurance date [possibly not
* present] can be used to determine that a s.transaction should be
* instantiated on a given date [the given query date].
*
* There is a split between the UIFreqType and the 'internal' FreqType
* to reduce the complexity of some of the code involved.
*
* Hmmm... having this in the private header file actually prevents
* client code from allocating this 'class' on the stack.
* Is that a problem?
*
* This still needs to deal with:
* . exceptions
* . yearly 360/365?
* . re-based frequencies [based around a non-standard [read:
* not-Jan-1-based/fiscal] year]
* . "business days" -- m-f sans holidays [per-user list thereof]
**/
struct gncp_freq_spec {
FreqType type;
UIFreqType uift;
union u {
struct {
GDate date; /** The date on which the single event occurs. */
} once;
struct {
guint interval_days; /** number of days from one repeat to the next. */
guint offset_from_epoch; /** epoch is defined by glib to be 1/1/1. Offset measured in days. 0 <= offset < interval */
} daily;
struct {
/* A week here is measured as 7 days. The first week starts at epoch.
* 1/1/1 was a ?. */
guint interval_weeks; /** number of weeks from one repeat to the next. */
guint offset_from_epoch; /* offset measured in days.
* This combines the week
* offset and the day of the
* week offset. */
/* guint offset_from_epoch;*/ /* offset measured in weeks, 0 <= offset < interval */
/* guint day_of_week;*/ /* I'm not sure what days each value represents, but it's not important. */
} weekly;
struct {
guint interval_months; /** number of months from one repeat to the next. */
guint offset_from_epoch; /* offset measured in months */
guint day_of_month; /* Which day of the month it occurs on. */
} monthly;
struct {
guint interval_months; /** Number of months from one repeat to the next. */
guint offset_from_epoch; /* offset measured in months */
guint weekday; /* stores a value equivalent to a GDateWeekday. */
guint occurrence; /* the 1st occurrence to the 5th occurrence. */
} month_relative;
struct {
/** A list of specs for a composite freq. */
GList *subSpecs;
} composites;
/**
* The dateAnchor anchors the spec to determinable days.
*
* ONCE:
* dA[0] contains time_t
* DAILY:
* dA[0] contains day multiplier
* dA[1] contains offset from epoch.
* WEEKLY:
* dA[0] contains week multiplier
* dA[1] contains 0..6 [sun-based]
* SEMI_MONTHLY: bstanley disused...
* dA[0] contains month multiplier
* dA[1] contains the first date-of-month,
* dA[2] the second.
* MONTHLY:
* dA[0] contains month multiplier
* dA[1] contains the date-of-month
* MONTH_RELATIVE:
* dA[0] continas month multiplier
* dA[1] contains week number [1..5, 6=="last"]
* [1..5 is really 1..4.428 [31/7], but it's a UI issue]
* dA[2] contains 0..6 [sun-based day of week]
* COMPOSITE:
* ... list ...
* RELATIVE:
* ... don't know yet ...
**/
} s;
GUID guid;
};
#endif /* XACC_FREQSPECP_H */

View File

@@ -53,7 +53,9 @@ typedef enum
GNC_ID_TRANS,
GNC_ID_SPLIT,
GNC_ID_PRICE,
LAST_GNC_ID = GNC_ID_PRICE
GNC_ID_SCHEDXACTION,
GNC_ID_FREQSPEC,
LAST_GNC_ID = GNC_ID_FREQSPEC
} GNCIdType;

View File

@@ -55,7 +55,11 @@ libgncengine_la_SOURCES = \
sixtp-to-dom-parser.c \
sixtp-utils.c \
sixtp-xml-write-utils.c \
sixtp.c
sixtp.c \
FreqSpec.c \
SchedXaction.c \
gnc-freqspec-xml-v2.c \
gnc-schedxaction-xml-v2.c
libgncengine_la_LDFLAGS = -version-info 2:3:1
@@ -107,7 +111,9 @@ noinst_HEADERS = \
sixtp-utils.h \
sixtp-writers.h \
sixtp-xml-write-utils.h \
sixtp.h
sixtp.h \
FreqSpec.h \
SchedXaction.h
EXTRA_DIST = \
.cvsignore \

316
src/engine/SchedXaction.c Normal file
View File

@@ -0,0 +1,316 @@
/********************************************************************\
* SchedXaction.c -- Scheduled Transaction implementation. *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#include "config.h"
#include <glib.h>
#include <string.h>
#include "SchedXaction.h"
#include "FreqSpec.h"
#include "GNCIdP.h"
#include "Transaction.h"
#include "TransactionP.h"
#include "date.h"
#include "gnc-engine-util.h"
#include "gnc-event-p.h"
#include "messages.h"
#include "Account.h"
#include "Group.h"
#include "guid.h"
#include "gnc-book.h"
#include "FileDialog.h"
static short module = MOD_SX;
/** Local data defs *****/
void transactionListMapDelete( gpointer data, gpointer user_data );
/** Local Prototypes *****/
SchedXaction*
xaccSchedXactionMalloc( GNCBook *book )
{
SchedXaction *sx;
sx = g_new0( SchedXaction, 1 );
xaccSchedXactionInit( sx, book );
gnc_engine_generate_event( &sx->guid, GNC_EVENT_CREATE );
return sx;
}
void
xaccSchedXactionInit( SchedXaction *sx, GNCBook *book )
{
Account *acct;
AccountGroup *ag;
char *name;
sx->freq = xaccFreqSpecMalloc();
xaccGUIDNew( &sx->guid );
xaccStoreEntity( sx, &sx->guid, GNC_ID_SCHEDXACTION );
g_date_clear( &sx->last_date, 1 );
g_date_clear( &sx->start_date, 1 );
g_date_clear( &sx->end_date, 1 );
sx->num_occurances_total = -1;
sx->kvp_data = kvp_frame_new();
sx->manual = FALSE;
sx->templateSplits = NULL;
// create a new template account for our splits...
acct = xaccMallocAccount();
name = guid_to_string( &sx->guid );
xaccAccountSetName( acct, name );
xaccAccountSetCurrency( acct,
gnc_commodity_new( "template", "template", "template", "template", 1 ) );
// FIXME: g_free?
// FIXME: leaks
// g_free( name );
xaccAccountSetType( acct, BANK );
ag = gnc_book_get_template_group( book );
xaccGroupInsertAccount( ag, acct );
}
void
xaccSchedXactionFree( SchedXaction *sx )
{
if ( sx == NULL ) return;
xaccFreqSpecFree( sx->freq );
gnc_engine_generate_event( &sx->guid, GNC_EVENT_DESTROY );
xaccRemoveEntity( &sx->guid );
g_list_foreach( sx->templateSplits,
transactionListMapDelete,
NULL );
g_list_free( sx->templateSplits );
if ( sx->name )
g_free( sx->name );
g_free( sx );
// FIXME: clean up our template account...
}
void
transactionListMapDelete( gpointer data, gpointer user_data )
{
Transaction *t = (Transaction*)data;
xaccTransBeginEdit( t );
xaccTransDestroy( t );
xaccTransCommitEdit( t );
}
FreqSpec *
xaccSchedXactionGetFreqSpec( SchedXaction *sx )
{
return sx->freq;
}
void
xaccSchedXactionSetFreqSpec( SchedXaction *sx, FreqSpec *fs )
{
g_return_if_fail( fs );
xaccFreqSpecFree( sx->freq );
sx->freq = fs;
}
gchar *
xaccSchedXactionGetName( SchedXaction *sx )
{
return sx->name;
}
void
xaccSchedXactionSetName( SchedXaction *sx, const gchar *newName )
{
GString *gstr;
g_return_if_fail( newName != NULL );
if ( sx->name != NULL ) {
g_free( sx->name );
}
gstr = g_string_new( newName );
sx->name = gstr->str;
g_string_free( gstr, FALSE );
}
GDate*
xaccSchedXactionGetStartDate( SchedXaction *sx )
{
return &sx->start_date;
}
void
xaccSchedXactionSetStartDate( SchedXaction *sx, GDate* newStart )
{
sx->start_date = *newStart;
}
gboolean
xaccSchedXactionHasEndDate( SchedXaction *sx )
{
return g_date_valid( &sx->end_date );
}
GDate*
xaccSchedXactionGetEndDate( SchedXaction *sx )
{
return &sx->end_date;
}
void
xaccSchedXactionSetEndDate( SchedXaction *sx, GDate *newEnd )
{
if ( g_date_valid( newEnd ) ) {
if ( g_date_compare( newEnd, &sx->start_date ) < 0 ) {
// FIXME:error
// error( "New end date before start date" );
}
}
sx->end_date = *newEnd;
}
GDate*
xaccSchedXactionGetLastOccurDate( SchedXaction *sx )
{
return &sx->last_date;
}
void
xaccSchedXactionSetLastOccurDate( SchedXaction *sx, GDate* newLastOccur )
{
sx->last_date = *newLastOccur;
}
gboolean
xaccSchedXactionHasOccurDef( SchedXaction *sx )
{
return ( xaccSchedXactionGetNumOccur( sx ) != 0 );
}
gint
xaccSchedXactionGetNumOccur( SchedXaction *sx )
{
return sx->num_occurances_total;
}
void
xaccSchedXactionSetNumOccur( SchedXaction *sx, gint newNum )
{
sx->num_occurances_remain = sx->num_occurances_total = newNum;
}
gint
xaccSchedXactionGetRemOccur( SchedXaction *sx )
{
return sx->num_occurances_remain;
}
void
xaccSchedXactionSetRemOccur( SchedXaction *sx,
gint numRemain )
{
/* FIXME This condition can be tightened up */
if ( numRemain > sx->num_occurances_total ) {
// FIXME:error
// error( "more remaining occurances than total" );
}
sx->num_occurances_remain = numRemain;
}
kvp_frame*
xaccSchedXactionGetSlots( SchedXaction *sx )
{
return sx->kvp_data;
}
void
xaccSchedXactionSetSlots( SchedXaction *sx, kvp_frame *frm )
{
sx->kvp_data = frm;
}
const GUID*
xaccSchedXactionGetGUID( SchedXaction *sx )
{
return &sx->guid;
}
void
xaccSchedXactionSetGUID( SchedXaction *sx, GUID g )
{
sx->guid = g;
}
void
xaccSchedXactionSetManual( SchedXaction *sx, gboolean newManual )
{
sx->manual = newManual;
}
int
xaccSchedXactionGetManual( SchedXaction *sx )
{
return sx->manual;
}
GDate
xaccSchedXactionGetNextInstance( SchedXaction *sx )
{
GDate last_occur, next_occur;
if ( g_date_valid( &sx->last_date ) ) {
last_occur = sx->last_date;
} else {
if ( g_date_valid( &sx->start_date ) ) {
last_occur = sx->start_date;
} else {
g_date_set_time( &last_occur, time(NULL) );
}
}
xaccFreqSpecGetNextInstance( sx->freq, &last_occur, &next_occur );
return next_occur;
}
GDate xaccSchedXactionGetInstanceAfter( SchedXaction *sx, GDate *date )
{
GDate next_occur;
xaccFreqSpecGetNextInstance( sx->freq, date, &next_occur );
return next_occur;
}
GList *
xaccSchedXactionGetSplits( SchedXaction *sx )
{
g_return_val_if_fail( sx, NULL );
return sx->templateSplits;
}
void
xaccSchedXactionSetSplits( SchedXaction *sx, GList *newSplits )
{
g_return_if_fail( sx );
sx->templateSplits = newSplits;
}

170
src/engine/SchedXaction.h Normal file
View File

@@ -0,0 +1,170 @@
/********************************************************************\
* SchedXaction.h -- Scheduled Transaction *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#ifndef XACC_SCHEDXACTION_H
#define XACC_SCHEDXACTION_H
#include "config.h"
#include <time.h>
#include <glib.h>
#include "GNCId.h"
#include "FreqSpec.h"
#include "date.h"
#include "kvp_frame.h"
#include "gnc-book.h"
/**
* A single scheduled transaction.
*
* Scheduled transactions have a list of transactions, and a frequency
* [and associated date anchors] with which they are scheduled.
*
* Things that make sense to have in a template transaction:
* [not] Date [though eventually some/multiple template transactions
* might have relative dates].
* Memo
* Account
* Funds In/Out... or an expr involving 'amt' [A, x, y, a?] for
* variable expenses.
*
* Template transactions are instantiated by:
* . copying the fields of the template
* . setting the date to the calculated "due" date.
*
* We should be able to use the GeneralLedger [or, yet-another-subtype
* of the internal ledger] for this editing.
**/
typedef struct gncp_SchedXaction {
gchar *name;
FreqSpec *freq;
GDate last_date;
GDate start_date;
// if end_date is invalid, then no end.
GDate end_date;
// if num_occurances_total == 0, then no limit
gint num_occurances_total;
// reminaing occurances are as-of the 'last_date'.
gint num_occurances_remain;
// If true, confirmation is required.
// If false, then this can be created when due without
// intervention.
gboolean manual;
GList *templateSplits;
GUID guid;
kvp_frame *kvp_data;
} SchedXaction;
/**
* Creates and initializes a scheduled transaction.
**/
SchedXaction *xaccSchedXactionMalloc( GNCBook *book );
/**
* Initially created with a null name, the default frequency, a
* start-date of today, no end date, no counted occurances, no
* templates transactions, and null kvp data.
**/
void xaccSchedXactionInit( SchedXaction *sx, GNCBook *book );
/**
* Cleans up and frees a SchedXaction and it's associated data.
**/
void xaccSchedXactionFree( SchedXaction *sx );
FreqSpec *xaccSchedXactionGetFreqSpec( SchedXaction *sx );
/**
* The FreqSpec is given to the SchedXaction for mem mgmt; it should
* not be freed by the external code.
**/
void xaccSchedXactionSetFreqSpec( SchedXaction *sx, FreqSpec *fs );
gchar *xaccSchedXactionGetName( SchedXaction *sx );
/**
* A copy of the name is made.
**/
void xaccSchedXactionSetName( SchedXaction *sx, const gchar *newName );
GDate* xaccSchedXactionGetStartDate( SchedXaction *sx );
void xaccSchedXactionSetStartDate( SchedXaction *sx, GDate* newStart );
int xaccSchedXactionHasEndDate( SchedXaction *sx );
/**
* Returns invalid date when there is no end-date specified.
**/
GDate* xaccSchedXactionGetEndDate( SchedXaction *sx );
void xaccSchedXactionSetEndDate( SchedXaction *sx, GDate* newEnd );
GDate* xaccSchedXactionGetLastOccurDate( SchedXaction *sx );
void xaccSchedXactionSetLastOccurDate( SchedXaction *sx, GDate* newLastOccur );
/**
* Returns true if the scheduled transaction has a defined number of
* occurances, false if not.
**/
gboolean xaccSchedXactionHasOccurDef( SchedXaction *sx );
gint xaccSchedXactionGetNumOccur( SchedXaction *sx );
void xaccSchedXactionSetNumOccur( SchedXaction *sx, gint numNum );
gint xaccSchedXactionGetRemOccur( SchedXaction *sx );
void xaccSchedXactionSetRemOccur( SchedXaction *sx, gint numRemain );
GList *xaccSchedXactionGetSplits( SchedXaction *sx );
void xaccSchedXactionSetSplits( SchedXaction *sx, GList *newSplits );
gboolean xaccSchedXactionGetManual( SchedXaction *sx );
void xaccSchedXactionSetManual( SchedXaction *sx, gboolean newManual );
#if 0
#error vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
GList *xaccSchedXactionGetXactions( SchedXaction *sx );
void xaccSchedXactionClearXactions( SchedXaction *sx );
void xaccSchedXactionAddXaction( SchedXaction *sx,
Transaction *t );
#error ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#endif // 0
kvp_frame *xaccSchedXactionGetSlots( SchedXaction *sx );
/**
* Sets the SX kvp data to the given kvp_frame.
* NOTE: This is not copied, but set directly.
**/
void xaccSchedXactionSetSlots( SchedXaction *sx,
kvp_frame *frm );
const GUID *xaccSchedXactionGetGUID( SchedXaction *sx );
void xaccSchedXactionSetGUID( SchedXaction *sx, GUID g );
/**
* Returns the next occurance of a scheduled transaction. If the
* transaction hasn't occured, then it's based off the start date.
* Otherwise, it's based off the last-occurance date.
**/
GDate xaccSchedXactionGetNextInstance( SchedXaction *sx );
GDate xaccSchedXactionGetInstanceAfter( SchedXaction *sx, GDate *date );
#endif /* XACC_SCHEDXACTION_H */

View File

@@ -689,5 +689,13 @@ gnc_timezone (struct tm *tm)
#endif
}
void
timespecFromTime_t( Timespec *ts, time_t t )
{
ts->tv_sec = t;
ts->tv_nsec = 0;
}
/********************** END OF FILE *********************************\
\********************************************************************/

View File

@@ -122,6 +122,11 @@ void printDateSecs (char * buff, time_t secs);
char * xaccPrintDateSecs (time_t secs);
const char * gnc_print_date(Timespec ts);
/**
* Turns a time_t into a Timespec
**/
void timespecFromTime_t( Timespec *ts, time_t t );
/**
* scanDate
* Convert a string into day / month / year integers according to

View File

@@ -326,6 +326,25 @@ gnc_account_end_handler(gpointer data_for_children,
return successful;
}
Account*
dom_tree_to_account( xmlNodePtr node )
{
Account *accToRet;
gboolean successful;
accToRet = xaccMallocAccount();
successful = dom_tree_generic_parse( node, account_handlers_v2, accToRet );
xaccAccountCommitEdit( accToRet );
if ( !successful ) {
xaccFreeAccount( accToRet );
accToRet = NULL;
}
// jsled_FIXME? See note above.
xaccAccountBeginEdit( accToRet );
return accToRet;
}
sixtp*
gnc_account_sixtp_parser_create(void)
{

View File

@@ -69,6 +69,8 @@ struct _gnc_book
{
AccountGroup *topgroup;
GNCPriceDB *pricedb;
GList *sched_xactions;
AccountGroup *template_group;
/* the requested book id, in the form or a URI, such as
* file:/some/where, or sql:server.host.com:555
@@ -157,13 +159,21 @@ gnc_book_pop_error (GNCBook * book)
/* ---------------------------------------------------------------------- */
const char *TEMPLATE_ACCOUNT_NAME = "__account for template transactions__";
static void
gnc_book_init (GNCBook *book)
{
Account *template_acct;
if(!book) return;
book->topgroup = xaccMallocAccountGroup();
book->pricedb = gnc_pricedb_create();
book->sched_xactions = NULL;
book->template_group = xaccMallocAccountGroup();
book->book_id = NULL;
gnc_book_clear_error (book);
book->fullpath = NULL;
@@ -241,6 +251,34 @@ gnc_book_set_pricedb(GNCBook *book, GNCPriceDB *db)
book->pricedb = db;
}
GList *
gnc_book_get_schedxactions( GNCBook *book )
{
if ( book == NULL ) return NULL;
return book->sched_xactions;
}
void
gnc_book_set_schedxactions( GNCBook *book, GList *newList )
{
if ( book == NULL ) return;
book->sched_xactions = newList;
}
AccountGroup *
gnc_book_get_template_group( GNCBook *book )
{
if ( book == NULL ) return NULL;
return book->template_group;
}
void
gnc_book_set_template_group( GNCBook *book, AccountGroup *templateGroup )
{
if ( book == NULL ) return;
book->template_group = templateGroup;
}
/* ---------------------------------------------------------------------- */
Backend *

View File

@@ -132,6 +132,15 @@ guint gnc_book_count_transactions(GNCBook *book);
*/
gnc_commodity_table* gnc_book_get_commodity_table(GNCBook *book);
/**
* Returns the list of scheduled transactions.
**/
GList * gnc_book_get_schedxactions( GNCBook *book );
void gnc_book_set_schedxactions( GNCBook *book, GList *newList );
AccountGroup *gnc_book_get_template_group( GNCBook *book );
void gnc_book_set_template_group( GNCBook *book, AccountGroup *templateGroup );
/* The gnc_book_get_file_path() routine returns the fully-qualified file
* path for the book. That is, if a relative or partial filename
* was for the book, then it had to have been fully resolved to

View File

@@ -61,7 +61,8 @@ typedef enum
MOD_EVENT = 13,
MOD_TXN = 14,
MOD_KVP = 15,
MOD_LAST = 15
MOD_SX = 16,
MOD_LAST = 16
} gncModuleType;
typedef enum

View File

@@ -0,0 +1,589 @@
/********************************************************************
* gnc-freqspec-xml-v2.c -- xml routines for FreqSpecs *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* Copyright (C) 2001 Ben Stanley <bds02@uow.edu.au> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
*******************************************************************/
#include "config.h"
#include <glib.h>
#include <string.h>
#include "gnc-xml-helper.h"
#include "gnc-engine-util.h"
#include "sixtp.h"
#include "sixtp-utils.h"
#include "sixtp-parsers.h"
#include "sixtp-utils.h"
#include "sixtp-xml-write-utils.h"
#include "sixtp-dom-parsers.h"
#include "sixtp-dom-generators.h"
#include "gnc-xml.h"
#include "io-gncxml-v2.h"
#include "sixtp-dom-parsers.h"
#include "SchedXaction.h"
#include "FreqSpecP.h"
/**
* The XML output should look something like:
* <freqspec>
* <fs:monthly>
* <fs:interval>2</fs:interval>
* <fs:offset>1</fs:offset>
* <fs:day>21</fs:day>
* </fs:monthly>
* <fs:id>...</fs:id>
* </freqspec>
*
* <freqspec>
*
* <fs:composite>
* <freqspec>
* <fs:weekly>
* <fs:interval>2</fs:interval>
* <fs:offset>3</fs:offset>
* </fs:weekly>
* </freqspec>
* <freqspec>
* <fs:weekly>
* <fs:interval>2</fs:interval>
* <fs:offset>12</fs:offset>
* </fs:weekly>
* </freqspec>
* </fs:composite>
* <fs:id>...</fs:id>
* </freqspec>
*
**/
const gchar *freqspec_version_string = "1.0.0";
struct freqTypeTuple {
char *str;
FreqType ft;
};
struct freqTypeTuple freqTypeStrs[] = {
{ "once", ONCE },
{ "daily", DAILY },
{ "weekly", WEEKLY },
{ "monthly", MONTHLY },
{ "month_relative", MONTH_RELATIVE },
{ "composite", COMPOSITE },
{ NULL, -1 },
};
struct uiFreqTypeTuple {
char *str;
UIFreqType uift;
};
struct uiFreqTypeTuple uiFreqTypeStrs[] = {
{ "none", UIFREQ_NONE },
{ "once", UIFREQ_ONCE },
{ "daily", UIFREQ_DAILY },
{ "daily_mf", UIFREQ_DAILY_MF },
{ "weekly", UIFREQ_WEEKLY },
{ "bi_weekly", UIFREQ_BI_WEEKLY },
{ "semi_monthly", UIFREQ_SEMI_MONTHLY },
{ "monthly", UIFREQ_MONTHLY },
{ "quarterly", UIFREQ_QUARTERLY },
{ "tri_anually", UIFREQ_TRI_ANUALLY },
{ "semi_yearly", UIFREQ_SEMI_YEARLY },
{ "yearly", UIFREQ_YEARLY },
{ NULL, -1 }
};
/**
* Struct passed around as user-data when parsing the FreqSpec.
**/
typedef struct _freqSpecParseData {
FreqSpec *fs; // FreqSpec we're parsing into.
// fields used in the union of unions... :)
GDate once_day; // once
gint64 interval; // all [except once]
gint64 offset; // all [except once]
gint64 day; // monthly or month-relative
gint64 occurrence; // month-relative
GList *list; // composite
} fsParseData;
void
_fspd_init( fsParseData *fspd )
{
fspd->fs = NULL;
fspd->list = NULL;
fspd->interval
= fspd->offset
= fspd->day
= fspd->occurrence
= 0;
g_date_clear( &fspd->once_day, 1 );
}
FreqSpec *dom_tree_to_freqSpec(xmlNodePtr node);
xmlNodePtr
gnc_freqSpec_dom_tree_create( FreqSpec *fs )
{
xmlNodePtr ret;
xmlNodePtr dateAnchorTmp;
xmlNodePtr xmlSub;
GString *tmpStr;
int i;
ret = xmlNewNode( NULL, "gnc:freqspec" );
xmlSetProp( ret, "version", freqspec_version_string );
xmlAddChild( ret, guid_to_dom_tree( "fs:id", &fs->guid ) );
xmlSub = text_to_dom_tree( "fs:ui_type",
uiFreqTypeStrs[ xaccFreqSpecGetUIType(fs) ].str );
xmlAddChild( ret, xmlSub );
switch( fs->type ) {
case ONCE: {
xmlSub = xmlNewNode( NULL, "fs:once" );
xmlAddChild( xmlSub,
gdate_to_dom_tree( "fs:date",
&fs->s.once.date ) );
xmlAddChild( ret, xmlSub );
} break;
case DAILY: {
xmlSub = xmlNewNode( NULL, "fs:daily" );
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:interval",
fs->s.daily.interval_days )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:offset",
fs->s.daily.offset_from_epoch )
);
xmlAddChild( ret, xmlSub );
} break;
case WEEKLY: {
xmlSub = xmlNewNode( NULL, "fs:weekly" );
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:interval",
fs->s.weekly.interval_weeks )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:offset",
fs->s.weekly.offset_from_epoch )
);
xmlAddChild( ret, xmlSub );
} break;
case MONTHLY: {
xmlSub = xmlNewNode( NULL, "fs:monthly" );
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:interval",
fs->s.monthly.interval_months )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:offset",
fs->s.monthly.offset_from_epoch )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:day",
fs->s.monthly.day_of_month )
);
xmlAddChild( ret, xmlSub );
} break;
case MONTH_RELATIVE: {
xmlSub = xmlNewNode( NULL, "fs:month_relative" );
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:interval",
fs->s.month_relative.interval_months )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:offset",
fs->s.month_relative.offset_from_epoch )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:weekday",
fs->s.month_relative.weekday )
);
xmlAddChild( xmlSub,
guint_to_dom_tree(
"fs:occurrence",
fs->s.month_relative.occurrence )
);
xmlAddChild( ret, xmlSub );
} break;
case COMPOSITE: {
GList *subelts;
xmlNodePtr xmlComposites;
xmlComposites = xmlNewNode( NULL, "fs:composite" );
subelts = fs->s.composites.subSpecs;
while( subelts ) {
xmlAddChild( xmlComposites,
gnc_freqSpec_dom_tree_create(
subelts->data ) );
subelts = subelts->next;
}
xmlAddChild( ret, xmlComposites );
} break;
case INVALID:
default:
g_return_val_if_fail( FALSE, NULL );
}
return ret;
}
static
gboolean
gnc_fs_handler( xmlNodePtr node, gpointer d )
{
// we ignore the wrapper... we were just called at the wrong
// level.
return TRUE;
}
static
gboolean
fs_uift_handler( xmlNodePtr node, fsParseData *fspd )
{
int i;
char *nodeTxt;
char *tmp;
nodeTxt = dom_tree_to_text( node );
g_return_val_if_fail( nodeTxt, FALSE );
for ( i=0; (tmp = uiFreqTypeStrs[i].str) != NULL; i++ ) {
if ( safe_strcmp( nodeTxt, tmp ) == 0 ) {
xaccFreqSpecSetUIType( fspd->fs, uiFreqTypeStrs[i].uift );
g_free( nodeTxt );
return TRUE;
}
}
g_free( nodeTxt );
return FALSE;
}
static
gboolean
fs_date_handler( xmlNodePtr node, fsParseData *fspd )
{
GDate *foo;
foo = dom_tree_to_gdate( node );
if ( foo == NULL )
return FALSE;
fspd->once_day = *foo;
g_date_free( foo );
return TRUE;
}
static
gboolean
fs_interval_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean ret;
gint64 foo;
ret = dom_tree_to_integer( node, &foo );
if ( ! ret ) {
return ret;
}
fspd->interval = foo;
return TRUE;
}
static
gboolean
fs_offset_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean ret;
gint64 foo;
ret = dom_tree_to_integer( node, &foo );
if ( ! ret )
return ret;
fspd->offset = foo;
return TRUE;
}
static
gboolean
fs_day_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean ret;
gint64 foo;
ret = dom_tree_to_integer( node, &foo );
if ( ! ret )
return ret;
fspd->day = foo;
return TRUE;
}
static
gboolean
fs_weekday_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean ret;
gint64 foo;
ret = dom_tree_to_integer( node, &foo );
if ( !ret )
return ret;
fspd->day = foo;
return TRUE;
}
static
gboolean
fs_occurrence_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean ret;
gint64 foo;
ret = dom_tree_to_integer( node, &foo );
if ( !ret )
return ret;
fspd->occurrence = foo;
return TRUE;
}
static
gboolean
fs_subelement_handler( xmlNodePtr node, fsParseData *fspd )
{
FreqSpec *fs;
gboolean successful;
fs = dom_tree_to_freqSpec( node );
if ( fs == NULL )
return FALSE;
fspd->list = g_list_append( fspd->list, fs );
return TRUE;
}
struct dom_tree_handler fs_union_dom_handlers[] = {
{ "fs:date", fs_date_handler, 0, 0 },
{ "fs:interval", fs_interval_handler, 0, 0 },
{ "fs:offset", fs_offset_handler, 0, 0 },
{ "fs:day", fs_day_handler, 0, 0 },
{ "fs:weekday", fs_weekday_handler, 0, 0 },
{ "fs:occurrence", fs_occurrence_handler, 0, 0 },
{ "gnc:freqspec", fs_subelement_handler, 0, 0 },
{ NULL, NULL, 0, 0 },
};
static
gboolean
fs_once_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean successful;
successful = dom_tree_generic_parse( node,
fs_union_dom_handlers,
fspd );
if ( !successful )
return FALSE;
fspd->fs->type = ONCE;
fspd->fs->s.once.date = fspd->once_day;
return TRUE;
}
static
gboolean
fs_daily_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean successful;
successful = dom_tree_generic_parse( node,
fs_union_dom_handlers,
fspd );
if ( !successful )
return FALSE;
fspd->fs->type = DAILY;
fspd->fs->s.daily.interval_days = fspd->interval;
fspd->fs->s.daily.offset_from_epoch = fspd->offset;
return TRUE;
}
static
gboolean
fs_weekly_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean successful;
successful = dom_tree_generic_parse( node,
fs_union_dom_handlers,
fspd );
if ( !successful )
return FALSE;
fspd->fs->type = WEEKLY;
fspd->fs->s.weekly.interval_weeks = fspd->interval;
fspd->fs->s.weekly.offset_from_epoch = fspd->offset;
return TRUE;
}
static
gboolean
fs_monthly_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean successful;
successful = dom_tree_generic_parse( node,
fs_union_dom_handlers,
fspd );
if ( !successful )
return FALSE;
fspd->fs->type = MONTHLY;
fspd->fs->s.monthly.interval_months = fspd->interval;
fspd->fs->s.monthly.offset_from_epoch = fspd->offset;
fspd->fs->s.monthly.day_of_month = fspd->day;
return successful;
}
static
gboolean
fs_month_relative_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean successful;
successful = dom_tree_generic_parse( node,
fs_union_dom_handlers,
fspd );
if ( !successful )
return FALSE;
fspd->fs->type = MONTH_RELATIVE;
fspd->fs->s.month_relative.interval_months = fspd->interval;
fspd->fs->s.month_relative.offset_from_epoch = fspd->offset;
fspd->fs->s.month_relative.weekday = fspd->day;
fspd->fs->s.month_relative.occurrence = fspd->occurrence;
return TRUE;
}
static
gboolean
fs_guid_handler( xmlNodePtr node, fsParseData *fspd )
{
GUID *guid;
guid = dom_tree_to_guid( node );
fspd->fs->guid = *guid;
return TRUE;
}
static
gboolean
fs_composite_handler( xmlNodePtr node, fsParseData *fspd )
{
gboolean successful;
successful = dom_tree_generic_parse( node,
fs_union_dom_handlers,
fspd );
if ( !successful )
return FALSE;
fspd->fs->type = COMPOSITE;
fspd->fs->s.composites.subSpecs = fspd->list;
return TRUE;
}
struct dom_tree_handler fs_dom_handlers[] = {
{ "gnc:freqspec", gnc_fs_handler, 0, 0 },
{ "fs:ui_type", fs_uift_handler, 1, 0 },
{ "fs:id", fs_guid_handler, 1, 0 },
{ "fs:once", fs_once_handler, 0, 0 },
{ "fs:daily", fs_daily_handler, 0, 0 },
{ "fs:weekly", fs_weekly_handler, 0, 0 },
{ "fs:monthly", fs_monthly_handler, 0, 0 },
{ "fs:month_relative", fs_month_relative_handler, 0, 0 },
{ "fs:composite", fs_composite_handler, 0, 0 },
{ NULL, NULL, 0, 0 }
};
static
gboolean
gnc_freqSpec_end_handler(gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer *result, const gchar *tag)
{
fsParseData fspd;
gboolean successful = FALSE;
xmlNodePtr aChild;
xmlNodePtr tree = (xmlNodePtr)data_for_children;
sixtp_gdv2 *globaldata = (sixtp_gdv2*)global_data;
_fspd_init( &fspd );
// this won't actually get invoked [FreqSpecs aren't top-level
// elements]; see dom_tree_to_freqSpec(), below.
if ( parent_data )
return TRUE;
if ( !tag )
return TRUE;
g_return_val_if_fail( tree, FALSE );
fspd.fs = xaccFreqSpecMalloc();
successful = dom_tree_generic_parse( tree, fs_dom_handlers, &fspd );
if (!successful) {
xmlElemDump( stdout, NULL, tree );
xaccFreqSpecFree( fspd.fs );
}
xmlFreeNode(tree);
return successful;
}
sixtp*
gnc_freqSpec_sixtp_parser_create(void)
{
return sixtp_dom_parser_new( gnc_freqSpec_end_handler, NULL, NULL );
}
FreqSpec*
dom_tree_to_freqSpec(xmlNodePtr node)
{
gboolean successful;
fsParseData fspd;
_fspd_init( &fspd );
fspd.fs = xaccFreqSpecMalloc();
successful = dom_tree_generic_parse( node, fs_dom_handlers, &fspd );
if ( !successful ) {
xaccFreqSpecFree( fspd.fs );
fspd.fs = NULL;
}
return fspd.fs;
}

View File

@@ -0,0 +1,443 @@
/********************************************************************
* gnc-schedxactions-xml-v2.c -- xml routines for transactions *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
*******************************************************************/
#include "config.h"
#include <glib.h>
#include <string.h>
#include "gnc-xml-helper.h"
#include "gnc-engine-util.h"
#include "sixtp.h"
#include "sixtp-utils.h"
#include "sixtp-xml-write-utils.h"
#include "sixtp-parsers.h"
#include "sixtp-utils.h"
#include "sixtp-dom-parsers.h"
#include "sixtp-dom-generators.h"
#include "gnc-xml.h"
#include "io-gncxml-v2.h"
#include "io-gncxml-gen.h"
#include "sixtp-dom-parsers.h"
#include "SchedXaction.h"
/**
* The XML output should look something like:
* <gnc:count-data cd:type="schedXaction">XXX</gnc:count-data>
* ...
* <gnc:schedxaction version="1.0.0">
* <sx:id type="guid">...</sx:id>
* <sx:name>Rent</sx:name>
* <sx:manual-conf>f</sx:manual-conf>
* <sx:lastOccur>
* <gdate>2001-02-28</gdate>
* </sx:lastOccur>
* <sx:start>
* <gdate>2000-12-31</gdate>
* </sx:start>
* <!-- no end -->
* <sx:freq>
* <!-- freq spec tree -->
* </sx:freq>
* </gnc:schedxaction>
* <gnc:schedxaction version="1.0.0">
* <sx:id type="guid">...</sx:id>
* <sx:name>Loan 1</sx:name>
* <sx:manual-conf>f</sx:manual-conf>
* <sx:start>
* <gdate>2000-12-31</gdate>
* </sx:start>
* <sx:end type="date">
* <gdate>2004-03-20</gdate>
* </sx:end>
* <sx:freq>
* <!-- freqspec tree -->
* </sx:freq>
* </gnc:schedxaction>
* <gnc:schedxaction version="1.0.0">
* <sx:id type="guid">...</sx:id>
* <sx:name>Loan 2</sx:name>
* <sx:manual-conf>f</sx:manual-conf>
* <sx:start>
* <gdate>2000-12-31</gdate>
* </sx:start>
* <sx:end type="num_occur">
* <sx:num>42</sx:num>
* </sx:end>
* <sx:freq>
* <!-- freqspec tree -->
* </sx:freq>
* </gnc:schedxaction>
*
* et-cetera...
* bleh.
**/
const gchar *schedxaction_version_string = "1.0.0";
xmlNodePtr
gnc_schedXaction_dom_tree_create(SchedXaction *sx)
{
xmlNodePtr ret;
xmlNodePtr fsNode;
Timespec ts;
GDate *date;
// FIXME: this should be the same as the def in io-gncxml-v2.c...
ret = xmlNewNode( NULL, "gnc:schedxaction" );
xmlSetProp( ret, "version", schedxaction_version_string );
xmlAddChild( ret,
guid_to_dom_tree("sx:id",
xaccSchedXactionGetGUID(sx)) );
xmlNewTextChild( ret, NULL, "sx:name", xaccSchedXactionGetName(sx) );
xmlNewTextChild( ret, NULL, "sx:manual-conf",
(xaccSchedXactionGetManual(sx) == 1 ? "t" : "f") );
xmlAddChild( ret,
gdate_to_dom_tree( "sx:start",
xaccSchedXactionGetStartDate(sx) ) );
date = xaccSchedXactionGetLastOccurDate(sx);
if ( g_date_valid( date ) ) {
xmlAddChild( ret, gdate_to_dom_tree( "sx:last", date ) );
}
if ( xaccSchedXactionHasOccurDef(sx) ) {
xml_add_gint32( ret, "sx:num-occur",
(gint32)xaccSchedXactionGetNumOccur(sx) );
xml_add_gint32( ret, "sx:rem-occur",
(gint32)xaccSchedXactionGetRemOccur(sx) );
} else if ( xaccSchedXactionHasEndDate(sx) ) {
xmlAddChild( ret,
gdate_to_dom_tree( "sx:end",
xaccSchedXactionGetEndDate(sx) ) );
}
// output freq spec
fsNode = xmlNewNode(NULL, "sx:freqspec");
xmlAddChild( fsNode,
gnc_freqSpec_dom_tree_create(
xaccSchedXactionGetFreqSpec(sx)) );
xmlAddChild( ret, fsNode );
// output kvp_frame
{
xmlNodePtr kvpnode =
kvp_frame_to_dom_tree( "sx:slots",
xaccSchedXactionGetSlots(sx) );
if ( kvpnode )
{
xmlAddChild(ret, kvpnode);
}
}
return ret;
}
static
gboolean
sx_id_handler( xmlNodePtr node, gpointer sx )
{
GUID *tmp = dom_tree_to_guid( node );
g_return_val_if_fail( tmp, FALSE );
xaccSchedXactionSetGUID( (SchedXaction*)sx, *tmp );
g_free( tmp );
return TRUE;
}
static
gboolean
sx_name_handler( xmlNodePtr node, gpointer sx )
{
gchar *tmp = dom_tree_to_text( node );
g_return_val_if_fail( tmp, FALSE );
xaccSchedXactionSetName( (SchedXaction*)sx, tmp );
g_free( tmp );
return TRUE;
}
static
gboolean
sx_set_date( xmlNodePtr node, SchedXaction *sx,
void (*settor)( SchedXaction *sx, GDate *d ) )
{
GDate *date;
date = dom_tree_to_gdate( node );
g_return_val_if_fail( date, FALSE );
(*settor)( sx, date );
g_date_free( date );
return TRUE;
}
static
gboolean
sx_start_handler( xmlNodePtr node, gpointer sx )
{
return sx_set_date( node, (SchedXaction*)sx,
xaccSchedXactionSetStartDate );
}
static
gboolean
sx_last_handler( xmlNodePtr node, gpointer sx )
{
return sx_set_date( node, (SchedXaction*)sx,
xaccSchedXactionSetLastOccurDate );
}
static
gboolean
sx_end_handler( xmlNodePtr node, gpointer sx )
{
return sx_set_date( node, (SchedXaction*)sx,
xaccSchedXactionSetEndDate );
}
static
gboolean
sx_freqspec_handler( xmlNodePtr node, gpointer sx )
{
xmlNodePtr mark;
FreqSpec *fs;
g_return_val_if_fail( node, FALSE );
fs = dom_tree_to_freqSpec( xmlGetLastChild( node ) );
xaccSchedXactionSetFreqSpec( (SchedXaction*)sx, fs );
return TRUE;
}
static
gboolean
sx_manualConf_handler( xmlNodePtr node, gpointer sx )
{
gchar *tmp;
tmp = dom_tree_to_text( node );
g_return_val_if_fail( tmp, FALSE );
xaccSchedXactionSetManual( (SchedXaction*)sx,
safe_strcmp(tmp, "t") == 0 );
g_free(tmp);
return TRUE;
}
static
gboolean
sx_numOccur_handler( xmlNodePtr node, gpointer sx )
{
gint64 numOccur;
if ( ! dom_tree_to_integer( node, &numOccur ) ) {
return FALSE;
}
xaccSchedXactionSetNumOccur( (SchedXaction*)sx, numOccur );
return TRUE;
}
static
gboolean
sx_remOccur_handler( xmlNodePtr node, gpointer sx )
{
gint64 remOccur;
if ( ! dom_tree_to_integer( node, &remOccur ) ) {
return FALSE;
}
xaccSchedXactionSetRemOccur( (SchedXaction*)sx, remOccur );
return TRUE;
}
static
gboolean
sx_slots_handler( xmlNodePtr node, gpointer sx )
{
kvp_frame *frm;
frm = dom_tree_to_kvp_frame( node );
xaccSchedXactionSetSlots( (SchedXaction*)sx, frm );
return TRUE;
}
struct dom_tree_handler sx_dom_handlers[] = {
{ "sx:id", sx_id_handler, 1, 0 },
{ "sx:name", sx_name_handler, 1, 0 },
{ "sx:start", sx_start_handler, 1, 0 },
{ "sx:last", sx_last_handler, 0, 0 },
{ "sx:manual-conf", sx_manualConf_handler, 1, 0 },
{ "sx:num-occur", sx_numOccur_handler, 0, 0 },
{ "sx:rem-occur", sx_remOccur_handler, 0, 0 },
{ "sx:end", sx_end_handler, 0, 0 },
{ "sx:freqspec", sx_freqspec_handler, 1, 0 },
{ "sx:slots", sx_slots_handler, 0, 0 },
};
static gboolean
gnc_schedXaction_end_handler(gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer *result, const gchar *tag)
{
SchedXaction *sx;
gboolean successful = FALSE;
xmlNodePtr achild;
xmlNodePtr tree = (xmlNodePtr)data_for_children;
gxpf_data *gdata = (gxpf_data*)global_data;
if ( parent_data ) {
return TRUE;
}
if ( !tag ) {
return TRUE;
}
g_return_val_if_fail( tree, FALSE );
sx = xaccSchedXactionMalloc( NULL );
successful = dom_tree_generic_parse( tree, sx_dom_handlers, sx );
if ( successful ) {
gdata->cb( tag, gdata->data, sx );
} else {
xmlElemDump( stdout, NULL, tree );
xaccSchedXactionFree( sx );
}
xmlFreeNode( tree );
return successful;
}
sixtp*
gnc_schedXaction_sixtp_parser_create(void)
{
return sixtp_dom_parser_new( gnc_schedXaction_end_handler, NULL, NULL );
}
static
gboolean
tt_act_handler( xmlNodePtr node, gnc_template_xaction_data *txd )
{
Account *acc;
acc = dom_tree_to_account( node );
if ( acc == NULL ) {
return FALSE;
} else {
txd->accts = g_list_append( txd->accts, acc );
}
return TRUE;
}
static
gboolean
tt_trn_handler( xmlNodePtr node, gnc_template_xaction_data *txd )
{
Transaction *trn;
trn = dom_tree_to_transaction( node );
if ( trn == NULL ) {
return FALSE;
} else {
txd->transactions = g_list_append( txd->transactions, trn );
}
return TRUE;
}
struct dom_tree_handler tt_dom_handlers[] = {
{ "gnc:account", tt_act_handler, 0, 0 },
{ "gnc:transaction", tt_trn_handler, 0, 0 },
};
static gboolean
gnc_template_transaction_end_handler(gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer *result, const gchar *tag)
{
gboolean successful = FALSE;
xmlNodePtr achild;
xmlNodePtr tree = (xmlNodePtr)data_for_children;
gxpf_data *gdata = (gxpf_data*)global_data;
GList *n;
gnc_template_xaction_data *txd =
g_new0( gnc_template_xaction_data, 1 );
// the DOM tree will have an account tree [the template group
// and account] and a list of transactions [which will be
// members of the template account].
// we want to parse through the dom trees for each, placing
// the null-parent account in the book's template-group slot,
// the others under it, and the transactions as normal.
if ( parent_data ) {
return TRUE;
}
if ( !tag ) {
return TRUE;
}
g_return_val_if_fail( tree, FALSE );
successful = dom_tree_generic_parse( tree, tt_dom_handlers, txd );
if ( successful ) {
gdata->cb( tag, gdata->data, txd );
} else {
xmlElemDump( stdout, NULL, tree );
}
// cleanup
for ( n = txd->accts; n; n = n->next ) {
n->data = NULL;
}
for ( n = txd->transactions; n; n = n->next ) {
n->data = NULL;
}
g_list_free( txd->accts );
g_list_free( txd->transactions );
g_free( txd );
xmlFreeNode( tree );
return successful;
}
sixtp*
gnc_template_transaction_sixtp_parser_create( void )
{
return sixtp_dom_parser_new( gnc_template_transaction_end_handler, NULL, NULL );
}

View File

@@ -510,6 +510,34 @@ gnc_transaction_end_handler(gpointer data_for_children,
return successful;
}
Transaction *
dom_tree_to_transaction( xmlNodePtr node )
{
Transaction *trn;
gboolean successful;
g_return_val_if_fail(node, FALSE);
trn = xaccMallocTransaction();
g_return_val_if_fail(trn, FALSE);
xaccTransBeginEdit(trn);
successful = dom_tree_generic_parse(node, trn_dom_handlers, trn);
xaccTransCommitEdit(trn);
if ( !successful )
{
xmlElemDump(stdout, NULL, node);
xaccTransBeginEdit(trn);
xaccTransDestroy(trn);
xaccTransCommitEdit(trn);
trn = NULL;
}
//xmlFreeNode(tree);
return trn;
}
sixtp*
gnc_transaction_sixtp_parser_create(void)

View File

@@ -35,6 +35,9 @@
#include "sixtp.h"
#include "gnc-pricedb.h"
#include "FreqSpec.h"
#include "SchedXaction.h"
xmlNodePtr gnc_account_dom_tree_create(Account *act);
sixtp* gnc_account_sixtp_parser_create(void);
@@ -50,5 +53,12 @@ Split* dom_tree_to_split(xmlNodePtr node);
xmlNodePtr gnc_pricedb_dom_tree_create(GNCPriceDB *db);
sixtp* gnc_pricedb_sixtp_parser_create(void);
xmlNodePtr gnc_schedXaction_dom_tree_create( SchedXaction *sx );
sixtp* gnc_schedXaction_sixtp_parser_create(void);
xmlNodePtr gnc_freqSpec_dom_tree_create( FreqSpec *fs );
sixtp* gnc_freqSpec_sixtp_parser_create(void);
sixtp* gnc_template_transaction_sixtp_parser_create(void);
#endif /* __GNC_XML_H__ */

View File

@@ -39,6 +39,8 @@
#include "Group.h"
#include "Transaction.h"
#define GNC_V2_STRING "gnc-v2"
static void
@@ -120,7 +122,58 @@ add_transaction_local(sixtp_gdv2 *data, Transaction *trn)
data->counter.transactions_loaded++;
run_callback(data, "transaction");
return TRUE;
}
static
gboolean
add_schedXaction_local(sixtp_gdv2 *data, SchedXaction *sx)
{
GList *list;
list = gnc_book_get_schedxactions( data->book );
list = g_list_append( list, sx );
gnc_book_set_schedxactions( data->book, list );
return TRUE;
}
static
gboolean
add_template_transaction_local( sixtp_gdv2 *data,
gnc_template_xaction_data *txd )
{
GList *n;
Account *tmpAcct;
AccountGroup *acctGroup;
// expect a struct of:
// . template accounts.
// . transactions in those accounts.
for ( n = txd->accts; n; n = n->next ) {
if ( xaccAccountGetParent( (Account*)n->data ) == NULL ) {
// remove the gnc_book_init-created account of
// the same name
acctGroup =
gnc_book_get_template_group( data->book );
tmpAcct =
xaccGetAccountFromName( acctGroup,
xaccAccountGetName( (Account*)n->data ) );
if ( tmpAcct != NULL ) {
xaccGroupRemoveAccount( acctGroup, tmpAcct );
}
xaccGroupInsertAccount( acctGroup, (Account*)n->data );
}
// This doesn't care about the "AccountCommitEdit-at-end"
// paradigm of the normal accounts/transactions so much,
// because there's only one template Account.
xaccAccountCommitEdit( (Account*)n->data );
}
for ( n = txd->transactions; n; n = n->next ) {
// insert transactions into accounts
add_transaction_local( data, (Transaction*)n->data );
}
return TRUE;
}
@@ -189,6 +242,10 @@ gnc_counter_end_handler(gpointer data_for_children,
{
sixdata->counter.commodities_total = val;
}
else if(safe_strcmp(type, "schedxaction") == 0)
{
sixdata->counter.schedXactions_total = val;
}
else
{
g_warning("Unknown type: %s",
@@ -218,6 +275,8 @@ print_counter_data(load_counter data)
data.accounts_total, data.accounts_loaded);
printf("Commodities: Total: %d, Loaded: %d\n",
data.commodities_total, data.commodities_loaded);
printf("Scheduled Tansactions: Total: %d, Loaded: %d\n",
data.schedXactions_total, data.schedXactions_loaded);
}
static const char *ACCOUNT_TAG = "gnc:account";
@@ -225,6 +284,8 @@ static const char *PRICEDB_TAG = "gnc:pricedb";
static const char *COMMODITY_TAG = "gnc:commodity";
static const char *COUNT_DATA_TAG = "gnc:count-data";
static const char *TRANSACTION_TAG = "gnc:transaction";
static const char *SCHEDXACTION_TAG = "gnc:schedxaction";
static const char *TEMPLATE_TRANSACTION_TAG = "gnc:template-transactions";
static gboolean
generic_callback(const char *tag, gpointer globaldata, gpointer data)
@@ -247,6 +308,14 @@ generic_callback(const char *tag, gpointer globaldata, gpointer data)
{
add_transaction_local(gd, (Transaction*)data);
}
else if(safe_strcmp(tag, SCHEDXACTION_TAG) == 0)
{
add_schedXaction_local(gd, (SchedXaction*)data);
}
else if(safe_strcmp(tag, TEMPLATE_TRANSACTION_TAG ) == 0 )
{
add_template_transaction_local( gd, (gnc_template_xaction_data*)data );
}
return TRUE;
}
@@ -270,6 +339,8 @@ gnc_book_load_from_xml_file_v2(
gd->counter.transactions_total = 0;
gd->counter.prices_loaded = 0;
gd->counter.prices_total = 0;
gd->counter.schedXactions_loaded = 0;
gd->counter.schedXactions_total = 0;
{
AccountGroup *g = gnc_book_get_group(book);
@@ -297,6 +368,8 @@ gnc_book_load_from_xml_file_v2(
COMMODITY_TAG, gnc_commodity_sixtp_parser_create(),
ACCOUNT_TAG, gnc_account_sixtp_parser_create(),
TRANSACTION_TAG, gnc_transaction_sixtp_parser_create(),
SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create(),
TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create(),
NULL, NULL))
{
return FALSE;
@@ -488,6 +561,39 @@ write_transactions(FILE *out, GNCBook *book)
(gpointer) out);
}
static void
write_template_transaction_data( FILE *out, GNCBook *book )
{
fprintf( out, "<%s>\n", TEMPLATE_TRANSACTION_TAG );
write_account_group( out, gnc_book_get_template_group(book) );
xaccGroupForEachTransaction( gnc_book_get_template_group(book),
xml_add_trn_data,
(gpointer)out );
fprintf( out, "</%s>\n", TEMPLATE_TRANSACTION_TAG );
}
static void
write_schedXactions( FILE *out, GNCBook *book )
{
GList *schedXactions;
SchedXaction *tmpSX;
xmlNodePtr node;
// get list of scheduled transactions from GNCBook
schedXactions = gnc_book_get_schedxactions( book );
if ( schedXactions == NULL )
return;
do {
tmpSX = schedXactions->data;
node = gnc_schedXaction_dom_tree_create( tmpSX );
xmlElemDump( out, NULL, node );
fprintf( out, "\n" );
xmlFreeNode( node );
} while ( (schedXactions = schedXactions->next) );
}
gboolean
gnc_book_write_to_xml_file_v2(GNCBook *book, const char *filename)
{
@@ -506,6 +612,8 @@ gnc_book_write_to_xml_file_v2(GNCBook *book, const char *filename)
xaccGroupGetNumSubAccounts(gnc_book_get_group(book)),
"transaction",
gnc_book_count_transactions(book),
"schedxaction",
g_list_length( gnc_book_get_schedxactions(book) ),
NULL);
write_commodities(out, book);
@@ -516,6 +624,10 @@ gnc_book_write_to_xml_file_v2(GNCBook *book, const char *filename)
write_transactions(out, book);
write_template_transaction_data(out, book);
write_schedXactions(out, book);
fprintf(out, "</" GNC_V2_STRING ">\n\n");
write_emacs_trailer(out);

View File

@@ -38,6 +38,7 @@
#include "Transaction.h"
#include "gnc-commodity.h"
#include "gnc-pricedb.h"
#include "SchedXaction.h"
struct _load_counter_struct
{
@@ -52,6 +53,9 @@ struct _load_counter_struct
int prices_total;
int prices_loaded;
int schedXactions_total;
int schedXactions_loaded;
};
typedef struct _load_counter_struct load_counter;
@@ -63,6 +67,18 @@ struct sixtp_global_data_v2_struct
void (*countCallback)(const char *type, load_counter counter);
};
/**
* Struct used to pass the account group/accounts and trasnactions in
* the <gnc:template-transactions> section between the parser in
* gnc-schedxactions-xml-v2.c and the add-to-book callback in
* io-gncxml-v2.c.
**/
typedef struct _gnc_template_xaction_data
{
GList *accts;
GList *transactions;
} gnc_template_xaction_data;
typedef struct sixtp_global_data_v2_struct sixtp_gdv2;
/* read in an account group from a file */
@@ -78,5 +94,4 @@ gboolean gnc_book_write_to_xml_file_v2(GNCBook *book, const char *filename);
*/
gboolean gnc_is_xml_data_file_v2(const gchar *name);
#endif /* __IO_GNCXML_V2_H__ */

View File

@@ -121,9 +121,27 @@ Type: gint64
Entities: Account
Use: A boolean flag indicated whether the Account is tax-related.
Name: sched-xaction
Type: frame
Entities: Split in a SchedXaction
Use: Storage for the various fields of a scheduled transaction's
template Splits.
Name: sched-xaction/xfrm
Type: GUID
Entities: Split
Use: The GUID of this Split's xfrm account.
Name: sched-xaction/amnt
Type: string
Entities: Split in a SchedXaction
Use: The amount field of a SchedXaction might be a formula, which we
store here.
Name: user-keys
Type: frame
Entities: All
Use: This frame is used to store keys which are editable directly by
the user. The program should not attach any semantics to keys
under this frame.

View File

@@ -130,6 +130,9 @@ gint double_compare(double v1, double v2);
void kvp_value_delete(kvp_value * value);
kvp_value * kvp_value_copy(const kvp_value * value);
/**
* Similar returns as strcmp.
**/
gint kvp_value_compare(const kvp_value *va, const kvp_value *vb);
/* list convenience funcs. */

View File

@@ -135,8 +135,7 @@ timespec_to_dom_tree(const char *tag, const Timespec *spec)
date_str = timespec_sec_to_string(spec);
if(!date_str)
{
if(!date_str) {
return NULL;
}
@@ -144,24 +143,41 @@ timespec_to_dom_tree(const char *tag, const Timespec *spec)
xmlNewTextChild(ret, NULL, "ts:date", date_str);
if(spec->tv_nsec > 0)
{
if(spec->tv_nsec > 0){
ns_str = timespec_nsec_to_string(spec);
if(ns_str)
{
if(ns_str){
xmlNewTextChild(ret, NULL, "ts:ns", ns_str);
}
}
g_free(date_str);
if(ns_str)
{
if(ns_str){
g_free(ns_str);
}
return ret;
}
xmlNodePtr
gdate_to_dom_tree(const char *tag, GDate *date)
{
xmlNodePtr ret;
gchar *date_str = NULL;
g_return_val_if_fail(date, NULL);
date_str = g_new( gchar, 512 );
g_date_strftime( date_str, 512, "%Y-%m-%d", date );
ret = xmlNewNode(NULL, tag);
xmlNewTextChild(ret, NULL, "gdate", date_str);
g_free(date_str);
return ret;
}
xmlNodePtr
gnc_numeric_to_dom_tree(const char *tag, const gnc_numeric *num)
{
@@ -331,3 +347,20 @@ kvp_frame_to_dom_tree(const char *tag, const kvp_frame *frame)
return ret;
}
xmlNodePtr guint_to_dom_tree(const char *tag, guint an_int)
{
xmlNodePtr ret;
gchar *numstr;
numstr = g_strdup_printf( "%u", an_int );
g_return_val_if_fail(numstr, NULL);
ret = xmlNewNode(NULL, tag);
xmlNodeAddContent(ret, numstr);
g_free(numstr);
return ret;
}

View File

@@ -44,8 +44,10 @@ xmlNodePtr commodity_ref_to_dom_tree(const char *tag, const gnc_commodity *c);
xmlNodePtr timespec_to_dom_tree(const char *tag, const Timespec *spec);
gchar * timespec_nsec_to_string(const Timespec *ts);
gchar * timespec_sec_to_string(const Timespec *ts);
xmlNodePtr gdate_to_dom_tree(const char *tag, GDate *spec);
xmlNodePtr gnc_numeric_to_dom_tree(const char *tag, const gnc_numeric *num);
xmlNodePtr kvp_frame_to_dom_tree(const char *tag, const kvp_frame *frame);
xmlNodePtr guint_to_dom_tree(const char *tag, guint an_int);
gchar* double_to_string(double value);

View File

@@ -419,6 +419,7 @@ dom_tree_to_text(xmlNodePtr tree)
return g_strdup("");
}
result = g_strdup("");
for(current = tree->xmlChildrenNode; current; current = current->next) {
@@ -570,6 +571,71 @@ dom_tree_to_timespec(xmlNodePtr node)
return ret;
}
GDate*
dom_tree_to_gdate(xmlNodePtr node)
{
/* Turn something like this
<sx:startdate>
<gdate>2001-04-03</gdate>
</sx:startdate>
into a GDate. If the xml is invalid, returns NULL. */
GDate *ret;
gboolean seen_date = FALSE;
xmlNodePtr n;
/* creates an invalid date */
ret = g_date_new();
for(n = node->xmlChildrenNode; n; n = n->next) {
switch(n->type) {
case XML_COMMENT_NODE:
break;
case XML_ELEMENT_NODE:
if(safe_strcmp("gdate", n->name) == 0) {
if(seen_date) {
goto failure;
} else {
gchar *content = dom_tree_to_text(n);
gint year, month, day;
if(!content) {
goto failure;
}
if(sscanf(content, "%d-%d-%d", &year, &month, &day ) != 3) {
g_free(content);
goto failure;
}
g_free(content);
seen_date = TRUE;
g_date_set_dmy( ret, day, month, year );
if( !g_date_valid( ret ) ) {
g_warning("dom_tree_to_gdate: invalid date");
goto failure;
}
}
}
break;
default:
PERR("dom_tree_to_gdate: unexpected sub-node.");
goto failure;
}
}
if(!seen_date) {
g_warning("dom_tree_to_gdate: no gdate node found.");
goto failure;
}
return ret;
failure:
g_date_free( ret );
return NULL;
}
gnc_commodity *
dom_tree_to_commodity_ref_no_engine(xmlNodePtr node)
{

View File

@@ -37,6 +37,10 @@
#include "GNCId.h"
#include "FreqSpec.h"
#include "Account.h"
#include "Transaction.h"
GUID* dom_tree_to_guid(xmlNodePtr node);
gnc_commodity* dom_tree_to_commodity_ref(xmlNodePtr node);
@@ -44,7 +48,10 @@ gnc_commodity* associate_commodity_ref_with_engine_commodity(
gnc_commodity *com);
gnc_commodity *dom_tree_to_commodity_ref_no_engine(xmlNodePtr node);
FreqSpec* dom_tree_to_freqSpec( xmlNodePtr node );
Timespec* dom_tree_to_timespec(xmlNodePtr node);
GDate* dom_tree_to_gdate(xmlNodePtr node);
gnc_numeric* dom_tree_to_gnc_numeric(xmlNodePtr node);
gchar * dom_tree_to_text(xmlNodePtr tree);
gboolean string_to_binary(const gchar *str, void **v, guint64 *data_len);
@@ -62,6 +69,9 @@ kvp_value* dom_tree_to_frame_kvp_value(xmlNodePtr node);
gboolean dom_tree_to_integer(xmlNodePtr node, gint64 *daint);
// jsled-added...
Account* dom_tree_to_account( xmlNodePtr node );
Transaction* dom_tree_to_transaction( xmlNodePtr node );
struct dom_tree_handler
{

View File

@@ -258,6 +258,11 @@ gnc_cm_event_handler (GUID *entity,
case GNC_ID_NONE:
break;
case GNC_ID_SCHEDXACTION:
case GNC_ID_FREQSPEC:
DEBUG( "FIXME for GNC_ID %d", id_type );
break;
default: {
char guid_str[GUID_ENCODING_LENGTH+1];
guid_to_string_buff (entity, guid_str);

View File

@@ -25,6 +25,7 @@ libgncgnome_a_SOURCES = \
dialog-transfer.c \
dialog-userpass.c \
dialog-utils.c \
dialog-scheduledxaction.c \
druid-commodity.c \
druid-hierarchy.c \
druid-qif-import.c \
@@ -38,6 +39,7 @@ libgncgnome_a_SOURCES = \
gnc-datedelta.c \
gnc-dateedit.c \
gnc-gpg.c \
gnc-frequency.c \
gnc-html-history.c \
gnc-html-guppi.c \
gnc-html.c \
@@ -56,7 +58,8 @@ libgncgnome_a_SOURCES = \
window-main-summarybar.c \
window-reconcile.c \
window-register.c \
window-report.c
window-report.c \
dialog-nextrun.c
gnomeappdir = ${datadir}/gnome/apps/Applications
@@ -83,6 +86,7 @@ noinst_HEADERS = \
dialog-totd.h \
dialog-transfer.h \
dialog-utils.h \
dialog-scheduledxaction.h \
druid-commodity.h \
druid-hierarchy.h \
druid-qif-import.h \
@@ -95,6 +99,7 @@ noinst_HEADERS = \
gnc-dateedit.h \
gnc-dir.h \
gnc-gpg.h \
gnc-frequency.h \
gnc-html-history.h \
gnc-html-guppi.h \
gnc-html.h \
@@ -113,7 +118,8 @@ noinst_HEADERS = \
window-main-summarybar.h \
window-reconcile.h \
window-register.h \
window-report.h
window-report.h \
dialog-nextrun.h
EXTRA_DIST = \
.cvsignore \

560
src/gnome/dialog-nextrun.c Normal file
View File

@@ -0,0 +1,560 @@
/********************************************************************\
* dialog-nextrun.c - "since last run" dialog. *
* Copyright (c) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
\********************************************************************/
#include "config.h"
#include <gnome.h>
#include <glib.h>
#include "gnc-engine-util.h"
#include "FileDialog.h"
#include "gnc-book.h"
#include "Transaction.h"
#include "Group.h"
#include "gnc-numeric.h"
#include "SchedXaction.h"
#include "gnc-component-manager.h"
#include "dialog-nextrun.h"
#include "SplitLedger.h"
#include "gnc-ui-util.h"
#include "gnc-exp-parser.h"
#include "dialog-utils.h"
#define DIALOG_NEXTRUN_CM_CLASS "dialog-nextrun"
static short module = MOD_SX;
typedef struct _toCreateTransaction {
SchedXaction *sx;
GDate *date;
gint clistRow;
} toCreateTransaction;
typedef struct _sxSinceLastData {
GtkWidget *nextrunDlg;
GladeXML *gxml;
GList /* <toCreateTransaction*> */ *transList;
} sxSinceLastData;
static void nextrun_init( sxSinceLastData *sxsld );
static void nextrun_close_handler( gpointer ud );
static void nr_ok_clicked( GtkButton *b, gpointer ud );
static void nr_next_clicked( GtkButton *b, gpointer ud );
static void nr_prev_xaction_clicked( GtkButton *b, gpointer ud );
static void nr_next_xaction_clicked( GtkButton *b, gpointer ud );
static void nextrun_destroy( GtkObject *o, gpointer ud );
static void slr_create_transactions( SchedXaction *sx, GDate *gd );
//int parse_vars_from_formula( const char *formula, GHashTable *varHash );
void
gnc_ui_nextrun_dialog_create(void)
{
sxSinceLastData *sxsld = g_new0( sxSinceLastData, 1 );
sxsld->gxml = gnc_glade_xml_new( "sched-xact.glade", "Since-Last-Run Instantiation" );
sxsld->nextrunDlg = glade_xml_get_widget( sxsld->gxml, "Since-Last-Run Instantiation" );
nextrun_init( sxsld );
}
static void
nextrun_init( sxSinceLastData *sxsld )
{
GtkObject *o;
int i;
struct widgetNameSignalHandlerTuple {
char *name;
char *signal;
void (*handlerFn)();
} widgets[] = {
{ "ok", "clicked", nr_ok_clicked },
{ "next", "clicked", nr_next_clicked },
{ "prev_xaction", "clicked", nr_prev_xaction_clicked },
{ "next_xaction", "clicked", nr_next_xaction_clicked },
{ NULL, NULL, NULL }
};
gnc_register_gui_component( DIALOG_NEXTRUN_CM_CLASS,
NULL,
nextrun_close_handler,
sxsld->nextrunDlg );
gtk_signal_connect( GTK_OBJECT(sxsld->nextrunDlg), "destroy",
GTK_SIGNAL_FUNC( nextrun_destroy ), sxsld );
for ( i=0; widgets[i].name != NULL ; i++ ) {
o = glade_xml_get_widget( sxsld->gxml, widgets[i].name );
gtk_signal_connect( o, widgets[i].signal,
GTK_SIGNAL_FUNC(widgets[i].handlerFn),
sxsld );
}
o = glade_xml_get_widget( sxsld->gxml, "next" );
gtk_signal_connect( o, "clicked",
GTK_SIGNAL_FUNC(nr_next_clicked),
sxsld );
o = glade_xml_get_widget( sxsld->gxml, "prev_xaction" );
gtk_signal_connect( o, "clicked",
GTK_SIGNAL_FUNC(nr_prev_xaction_clicked),
sxsld );
o = glade_xml_get_widget( sxsld->gxml, "next_xaction" );
gtk_signal_connect( o, "clicked",
GTK_SIGNAL_FUNC(nr_next_xaction_clicked),
sxsld );
gtk_widget_show_all( sxsld->nextrunDlg );
}
static void
nextrun_close_handler( gpointer ud )
{
gnome_dialog_close( GNOME_DIALOG( ((sxSinceLastData*)ud)->nextrunDlg ) );
}
static void
nr_ok_clicked( GtkButton *b, gpointer ud )
{
sxSinceLastData *sxsld;
GList *tctList;
toCreateTransaction *tct;
sxsld = (sxSinceLastData*)ud;
tctList = sxsld->transList;
if ( tctList == NULL ) {
PERR( "no transactions to create\n" );
}
do {
tct = (toCreateTransaction*)tctList->data;
slr_create_transactions( tct->sx, tct->date );
} while ( (tctList = tctList->next) );
nextrun_close_handler( ud );
}
static void
free_elts( gpointer data, gpointer user_data )
{
g_free( data );
}
static void
nr_next_clicked( GtkButton *b, gpointer ud )
{
sxSinceLastData *sxsld;
GtkWidget *dlg;
GtkObject *o;
GtkCList *cl;
time_t gdeDate;
GList *sxList;
GNCBook *book;
SchedXaction *sx;
GDate gd, *endDate;
gchar buf[1024];
gint row;
char *rowText[2];
toCreateTransaction *tct;
sxsld = (sxSinceLastData*)ud;
o = glade_xml_get_widget( sxsld->gxml, "next_date" );
gdeDate = gnome_date_edit_get_date( GNOME_DATE_EDIT(o) );
DEBUG( "Okay... I should run with a date of: %s", ctime(&gdeDate) );
// destroy the previous transactions
// destroy all the toCreateTransactions
if ( sxsld->transList != NULL ) {
g_list_foreach( sxsld->transList, free_elts, NULL );
g_list_free( sxsld->transList );
sxsld->transList = NULL;
}
book = gncGetCurrentBook();
sxList = gnc_book_get_schedxactions( book );
if ( sxList == NULL ) {
PERR( "No scheduled transactions to play with\n" );
return;
}
endDate = g_date_new();
g_date_set_time( endDate, gdeDate );
o = glade_xml_get_widget( sxsld->gxml, "replace_with_register" );
cl = GTK_CLIST(o);
gtk_clist_clear( cl );
gd = *g_date_new();
row = 0;
do {
sx = (SchedXaction*)sxList->data;
g_date_set_time( &gd, time(NULL) );
while ( g_date_compare( &gd, endDate ) <= 0 ) {
g_date_strftime( buf, 1023, "%c", &gd );
// add to clist [ahem... register... ahem]
rowText[0] = xaccSchedXactionGetName( sx );
rowText[1] = malloc( sizeof(char) * 256 ); // FIXME
g_date_strftime( rowText[1], 255, "%c", &gd );
tct = g_new0( toCreateTransaction, 1 );
tct->sx = sx;
tct->date = g_date_new();
*tct->date = gd;
tct->clistRow = row;
sxsld->transList = g_list_append( sxsld->transList, tct );
gtk_clist_insert( cl, row, rowText );
row += 1;
gd = xaccSchedXactionGetInstanceAfter( sx, &gd );
}
} while ( (sxList = sxList->next) );
#if 0
sxList = sxsld->transList;
do {
char buf[128];
g_date_strftime( buf, 127, "%c",
((toCreateTransaction*)sxList->data)->date );
DEBUG( "List contains sx \"%s\" on date \"%s\"\n",
xaccSchedXactionGetName( ((toCreateTransaction*)sxList->data)->sx ),
buf );
} while ( (sxList = sxList->next) );
#endif // 0
}
static void
nextrun_destroy( GtkObject *o, gpointer ud )
{
DEBUG( "nuttin' doin...\n" );
}
static void
nr_prev_xaction_clicked( GtkButton *b, gpointer ud )
{
}
static void
nr_next_xaction_clicked( GtkButton *b, gpointer ud )
{
}
static gboolean
create_each_transaction( Transaction *t, void *d )
{
Transaction *newT;
GDate *gd;
GList *sList;
GList *osList;
Split *split;
kvp_frame *split_kvpf;
kvp_value *kvp_val;
gboolean errFlag;
errFlag = FALSE;
DEBUG( "I'm seeing Transaction \"%s\"\n",
xaccTransGetDescription( t ) );
gd = (GDate*)d;
newT = xaccMallocTransaction();
xaccTransBeginEdit( newT );
// the action and description/memo are in the template
gnc_copy_trans_onto_trans( t, newT, FALSE, FALSE );
// the date is new [gd]
xaccTransSetDate( newT,
g_date_day( gd ),
g_date_month( gd ),
g_date_year( gd ) );
// the accounts and amounts are in the kvp_frames of the splits.
osList = xaccTransGetSplitList( t );
sList = xaccTransGetSplitList( newT );
if ( (osList == NULL) || (sList == NULL) ) {
PERR( "\tseen transaction w/o splits. :(\n" );
return FALSE;
}
do {
split = (Split*)sList->data;
// Ick. This assumes that the split lists will be
// ordered identically. :( I think it's fair to say
// they will, but I'd rather not have to count on
// it. --jsled
split_kvpf = xaccSplitGetSlots( (Split*)osList->data );
DEBUG( "\tProcessing Split \"%s\"\n",
xaccSplitGetMemo( split ) );
DEBUG( "\tkvp_frame: %s\n",
kvp_frame_to_string( split_kvpf ) );
// from-transaction of splits
{
GUID *acct_guid;
Account *acct;
// contains the guid of the split's actual account.
kvp_val = kvp_frame_get_slot( split_kvpf, "sched-xaction/xfrm" );
if ( kvp_val == NULL ) {
PERR( "Null kvp_val for xfrm\n" );
}
acct_guid = kvp_value_get_guid( kvp_val );
acct = xaccAccountLookup( acct_guid );
DEBUG( "Got account with name \"%s\"\n",
xaccAccountGetName( acct ) );
//xaccSplitSetAccount( split, acct );
xaccAccountInsertSplit( acct, split );
}
// credit/debit formulas
{
char *str;
gnc_numeric credit_num;
gnc_numeric debit_num;
gnc_numeric final;
int gncn_error;
kvp_val = kvp_frame_get_slot( split_kvpf, "sched-xaction/credit_formula" );
str = kvp_value_get_string( kvp_val );
credit_num = gnc_numeric_create( 0, 1 );
if ( str != NULL ) {
printf( "---------------\n" );
printf( "Parsing formula:\n" );
//parse_vars_from_formula( str, NULL );
printf( "---------------\n" );
xaccParseAmount( str, TRUE, &credit_num, NULL );
//string_to_gnc_numeric( str, &credit_num );
printf( "gnc_numeric::credit: \"%s\" -> \"%s\"\n",
str, gnc_numeric_to_string( credit_num ) );
}
kvp_val = kvp_frame_get_slot( split_kvpf, "sched-xaction/debit_formula" );
str = kvp_value_get_string( kvp_val );
debit_num = gnc_numeric_create( 0, 1 );
if ( str != NULL ) {
printf( "---------------\n" );
printf( "Parsing formula:\n" );
//parse_vars_from_formula( str, NULL );
printf( "---------------\n" );
xaccParseAmount( str, TRUE, &debit_num, NULL );
//string_to_gnc_numeric( str, &debit_num );
printf( "gnc_numeric::debit: \"%s\" -> \"%s\"\n",
str, gnc_numeric_to_string( debit_num ) );
}
final = gnc_numeric_sub_fixed( debit_num,
credit_num );
gncn_error = gnc_numeric_check( final );
if ( gncn_error != GNC_ERROR_OK ) {
printf( "Error %d in final gnc_numeric value\n", gncn_error );
errFlag = TRUE;
break;
}
printf( "gnc_numeric::final: \"%s\"\n",
gnc_numeric_to_string( final ) );
xaccSplitSetValue( split, final );
}
#if 0
kvp_val = kvp_frame_get_slot( split_kvpf, "sched-xaction/shares" );
kvp_val = kvp_frame_get_slot( split_kvpf, "sched-xaction/amnt" );
#endif // 0
} while ( (sList = sList->next) && (osList = osList->next) );
if ( errFlag ) {
printf( "Some error in newT creation\n" );
xaccTransRollbackEdit( newT );
} else {
xaccTransCommitEdit( newT );
}
return TRUE;
}
static void
slr_create_transactions( SchedXaction *sx, GDate *gd )
{
AccountGroup *ag;
Account *acct;
char *id;
// get template account group
ag = gnc_book_get_template_group( gncGetCurrentBook() );
id = guid_to_string( xaccSchedXactionGetGUID(sx) );
acct = xaccGetAccountFromName( ag, id );
printf( "Got account \"%s\"\n",
xaccAccountGetName( acct ) );
g_free( id );
xaccAccountForEachTransaction( acct,
create_each_transaction,
gd );
}
#if 0
/**
* Parses in-fix mathematical formulas using the standard operators
* [+-/%*], and '(', ')' grouping.
*
* Any strings are placed in the GHashTable as variables. The value of
* each key in the hash-table is a struct of the form:
* { const char *varStr;
* gint idx;
* gint len; };
*
**/
int
parse_vars_from_formula( const char *formula, GHashTable *varHash )
{
gnc_numeric num_foo;
char *foo;
GList *list;
gnc_exp_parser_init();
if ( ! gnc_exp_parser_parse( formula, &num_foo, &foo ) ) {
printf( "Error parsing at \"%s\": %s\n",
foo, gnc_exp_parser_error_string() );
}
printf( "Successful parse...\n" );
list = gnc_exp_parser_get_variable_names();
if ( list == NULL ) {
printf( "NULL variable list\n" );
} else {
do {
printf( "Variable \"%s\"\n",
list->data );
} while ( (list = list->next) );
}
gnc_exp_parser_shutdown();
}
#endif // 0
#if 0
GScanner *varScanner;
GTokenType tok;
varScanner = g_scanner_new( NULL );
g_scanner_set_scope( varScanner, 0 );
g_scanner_freeze_symbol_table( varScanner );
g_scanner_scope_add_symbol( varScanner, 0, "(", (gpointer)"left-paren" );
g_scanner_scope_add_symbol( varScanner, 0, ")", (gpointer)"right-paren" );
g_scanner_scope_add_symbol( varScanner, 0, "+", (gpointer)"plus" );
g_scanner_scope_add_symbol( varScanner, 0, "-", (gpointer)"minus" );
g_scanner_scope_add_symbol( varScanner, 0, "/", (gpointer)"div" );
g_scanner_scope_add_symbol( varScanner, 0, "*", (gpointer)"mult" );
g_scanner_thaw_symbol_table( varScanner );
g_scanner_input_text( varScanner, formula, strlen( formula ) );
do {
tok = g_scanner_get_next_token( varScanner );
printf( "tok: " );
switch ( varScanner->token ) {
case G_TOKEN_EOF:
printf( "EOF" ); break;
case G_TOKEN_LEFT_PAREN:
printf( "(" ); break;
case G_TOKEN_RIGHT_PAREN:
printf( ")" ); break;
case G_TOKEN_LEFT_CURLY:
printf( "{" ); break;
case G_TOKEN_RIGHT_CURLY:
printf( "}" ); break;
case G_TOKEN_LEFT_BRACE:
printf( "[" ); break;
case G_TOKEN_RIGHT_BRACE:
printf( "]" ); break;
case G_TOKEN_EQUAL_SIGN:
printf( "=" ); break;
case G_TOKEN_COMMA:
printf( "," ); break;
case G_TOKEN_NONE:
printf( "NONE" ); break;
case G_TOKEN_ERROR:
printf( "ERROR(%d)", varScanner->value.v_error ); break;
case G_TOKEN_CHAR:
printf( "CHAR(%c)", varScanner->value.v_char ); break;
case G_TOKEN_BINARY:
printf( "BINARY" ); break;
case G_TOKEN_OCTAL:
printf( "OCTAL(%ul)", varScanner->value.v_octal ); break;
case G_TOKEN_INT:
printf( "INT(%ul)", varScanner->value.v_int ); break;
case G_TOKEN_HEX:
printf( "HEX(%ul)", varScanner->value.v_hex ); break;
case G_TOKEN_FLOAT:
printf( "FLOAT(%f)", varScanner->value.v_float ); break;
case G_TOKEN_STRING:
printf( "STRING(%s)", varScanner->value.v_string ); break;
case G_TOKEN_SYMBOL:
printf( "SYMBOL(%s)", (gchar*)varScanner->value.v_symbol ); break;
case G_TOKEN_IDENTIFIER:
printf( "IDENT(%s)", varScanner->value.v_identifier ); break;
case G_TOKEN_IDENTIFIER_NULL:
printf( "NULL_IDENT" ); break;
case G_TOKEN_COMMENT_SINGLE:
case G_TOKEN_COMMENT_MULTI:
printf( "COMMENT(%s)", varScanner->value.v_comment ); break;
case G_TOKEN_LAST:
printf( "END" ); break;
default:
printf( "UNK" ); break;
};
printf( "\n" );
} while ( (varScanner->token != G_TOKEN_LAST) &&
(varScanner->token != G_TOKEN_EOF) &&
(varScanner->token != G_TOKEN_NONE) );
return 1;
/*
"0.33 * ( base + ld ) + (0.25 * internet)":
tok: FLOAT(0.330000)
tok: UNK
tok: )
tok: IDENT(base)
tok: UNK
tok: IDENT(ld)
tok: (
tok: UNK
tok: )
tok: FLOAT(0.250000)
tok: UNK
tok: IDENT(internet)
tok: (
tok: EOF
*/
#endif // 0

View File

@@ -0,0 +1,23 @@
/********************************************************************\
* dialog-nextrun.h - Beginnings of "since last run" dialog. *
* Copyright (c) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
\********************************************************************/
void gnc_ui_nextrun_dialog_create( void );

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/********************************************************************\
* dialog-scheduledxaction.h : dialogs for a scheduled transactions *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
\********************************************************************/
#ifndef __DIALOG_SCHEDULEDXACTION_H_
#define __DIALOG_SCHEDULEDXACTION_H_
#include "config.h"
#include <gnome.h>
#include "SchedXaction.h"
struct _SchedXactionDialog;
struct _SchedXactionEditorDialog;
typedef struct _SchedXactionDialog SchedXactionDialog;
typedef struct _SchedXactionEditorDialog SchedXactionEditorDialog;
SchedXactionDialog * gnc_ui_scheduled_xaction_dialog_create(void);
void gnc_ui_scheduled_xaction_dialog_destroy(SchedXactionDialog *sxd);
void row_select_handler( GtkCList *clist, gint row, gint col, GdkEventButton *event, gpointer d );
SchedXactionEditorDialog *
gnc_ui_scheduled_xaction_editor_dialog_create( SchedXactionDialog *sxd,
SchedXaction *sx );
void gnc_ui_scheduled_xaction_editor_dialog_destroy(SchedXactionEditorDialog *sxd);
#endif
/**
* TODO:
* . date-entries should back-stop each other?
* . modify gtkentry to prohibit fracation num-occurance values.
**/

View File

@@ -15,7 +15,8 @@ glade_DATA = \
stocks.glade \
tax.glade \
transfer.glade \
userpass.glade
userpass.glade \
sched-xact.glade
STRING_FILES = \
account_strings.c \
@@ -33,6 +34,7 @@ STRING_FILES = \
stocks_strings.c \
tax_strings.c \
transfer_strings.c \
userpass_strings.c
userpass_strings.c \
sched-xact_strings.c
EXTRA_DIST = $(glade_DATA) $(STRING_FILES)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,349 @@
/*
* Translatable strings file generated by Glade.
* Add this file to your project's POTFILES.in.
* DO NOT compile it as part of your application.
*/
gchar *s = N_("Edit Scheduled Transaction");
gchar *s = N_("Name:");
gchar *s = N_("Creation Options");
gchar *s = N_("Create automatically.");
gchar *s = N_("Notify me when created.");
gchar *s = N_("End");
gchar *s = N_("No End");
gchar *s = N_("End Date:");
gchar *s = N_("Number of Occurances:");
gchar *s = N_("Recurrance Frequency");
gchar *s = N_("Template Transaction");
gchar *s = N_("Record");
gchar *s = N_("REPLACEME with the Register control box...");
gchar *s = N_("This window should never be realized.");
gchar *s = N_("Frequency:");
gchar *s = N_("None");
gchar *s = N_("Once");
gchar *s = N_("Daily");
gchar *s = N_("Daily [M-F]");
gchar *s = N_("Weekly");
gchar *s = N_("Bi-Weekly");
gchar *s = N_("Semi-Monthly");
gchar *s = N_("Monthly");
gchar *s = N_("Quarterly");
gchar *s = N_("Tri-Anually");
gchar *s = N_("Semi-Yearly");
gchar *s = N_("Yearly");
gchar *s = N_("Start Date:");
gchar *s = N_("Not scheduled");
gchar *s = N_("None");
gchar *s = N_("Select occurrence date above.");
gchar *s = N_("Once");
gchar *s = N_("Every ");
gchar *s = N_("days.");
gchar *s = N_("Example");
gchar *s = N_("Daily");
gchar *s = N_("Every ");
gchar *s = N_("weeks.");
gchar *s = N_("Example");
gchar *s = N_("Daily [M-F]");
gchar *s = N_("Every");
gchar *s = N_("weeks.");
gchar *s = N_("Days");
gchar *s = N_("Sunday");
gchar *s = N_("Monday");
gchar *s = N_("Tuesday");
gchar *s = N_("Wednesday");
gchar *s = N_("Thursday");
gchar *s = N_("Friday");
gchar *s = N_("Saturday");
gchar *s = N_("Example");
gchar *s = N_("Weekly");
gchar *s = N_("Select initial date, above.");
gchar *s = N_("Bi-Weekly");
gchar *s = N_("Every");
gchar *s = N_("months.");
gchar *s = N_("First on the:");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("then on the:");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("Example");
gchar *s = N_("Semi-Monthly");
gchar *s = N_("Every ");
gchar *s = N_("months.");
gchar *s = N_("On the");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("Example");
gchar *s = N_("Monthly");
gchar *s = N_("Occuring in");
gchar *s = N_("Jan, Apr, Jul, Oct");
gchar *s = N_("Feb, May, Aug, Nov");
gchar *s = N_("Mar, Jun, Sep, Dec");
gchar *s = N_("On the");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("Quarterly");
gchar *s = N_("Occuring in");
gchar *s = N_("Jan, May, Sep");
gchar *s = N_("Feb, Apr, Oct");
gchar *s = N_("Mar, Jun, Nov");
gchar *s = N_("Apr, Jul, Dec");
gchar *s = N_("On the");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("Tri-Yearly");
gchar *s = N_("Occuring in");
gchar *s = N_("Jan, Jul");
gchar *s = N_("Feb, Aug");
gchar *s = N_("Mar, Sep");
gchar *s = N_("Apr, Oct");
gchar *s = N_("May, Nov");
gchar *s = N_("Jun, Dec");
gchar *s = N_("On the");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("Semi-Yearly");
gchar *s = N_("Every");
gchar *s = N_("year(s).");
gchar *s = N_("Month");
gchar *s = N_("January");
gchar *s = N_("February");
gchar *s = N_("March");
gchar *s = N_("April");
gchar *s = N_("May");
gchar *s = N_("June");
gchar *s = N_("July");
gchar *s = N_("August");
gchar *s = N_("September");
gchar *s = N_("October");
gchar *s = N_("November");
gchar *s = N_("December");
gchar *s = N_("Day");
gchar *s = N_("1st");
gchar *s = N_("2nd");
gchar *s = N_("3rd");
gchar *s = N_("4th");
gchar *s = N_("5th");
gchar *s = N_("6th");
gchar *s = N_("7th");
gchar *s = N_("8th");
gchar *s = N_("9th");
gchar *s = N_("10th");
gchar *s = N_("11th");
gchar *s = N_("12th");
gchar *s = N_("13th");
gchar *s = N_("14th");
gchar *s = N_("15th");
gchar *s = N_("16th");
gchar *s = N_("17th");
gchar *s = N_("18th");
gchar *s = N_("19th");
gchar *s = N_("20th");
gchar *s = N_("21st");
gchar *s = N_("22nd");
gchar *s = N_("23rd");
gchar *s = N_("24th");
gchar *s = N_("25th");
gchar *s = N_("26th");
gchar *s = N_("27th");
gchar *s = N_("28th");
gchar *s = N_("[29th]");
gchar *s = N_("[30th]");
gchar *s = N_("[31st]");
gchar *s = N_("[last day]");
gchar *s = N_("Yearly");
gchar *s = N_("Scheduled Transactions");
gchar *s = N_("Name");
gchar *s = N_("Frequency");
gchar *s = N_("Next");
gchar *s = N_("New");
gchar *s = N_("Edit");
gchar *s = N_("Delete");
gchar *s = N_("Since Last Run...");
gchar *s = N_("Next run date...");
gchar *s = N_("To-create...");
gchar *s = N_("Name");
gchar *s = N_("Date");
gchar *s = N_("Variable");
gchar *s = N_("Value");
gchar *s = N_("What should this be?");

View File

@@ -7,6 +7,12 @@
* This file was part of the Gnome Library. It was modifed by
* Dave Peticolas <peticola@cs.ucdavis.edu> for use in GnuCash.
*
* 2001.05.13T1647 [PDT], #gnucash:
* <jsled> dave_p: So the header for gnc-dateedit.h is a bit light
* on _why_ such a thing was done... any help?
* <dave_p> jsled: gnome date edit isn't i18n'd properly. also, we
* added the register date hotkeys.
*
* GnuCash is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of
@@ -23,8 +29,7 @@
* Free Software Foundation Voice: +1-617-542-5942
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652
* Boston, MA 02111-1307, USA gnu@gnu.org
*
*/
* */
/*
@NOTATION@
*/

1014
src/gnome/gnc-frequency.c Normal file

File diff suppressed because it is too large Load Diff

89
src/gnome/gnc-frequency.h Normal file
View File

@@ -0,0 +1,89 @@
/********************************************************************\
* gnc-frequency.h -- GnuCash widget for frequency editing. *
* Copyright (C) 2001 Joshua Sled <jsled@asynchronous.org> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#ifndef GNC_FREQUENCY_H
#define GNC_FREQUENCY_H
#include <gnome.h>
#include "FreqSpec.h"
#include "dialog-utils.h"
BEGIN_GNOME_DECLS
#define GNC_FREQUENCY(obj) GTK_CHECK_CAST(obj, gnc_frequency_get_type(), GNCFrequency)
#define GNC_FREQENCY_CLASS(klass) GTK_CHECK_CLASS_CAST(klass, gnc_frequency_get_type(), GNCFrequency)
#define GNC_IS_FREQUENCY(obj) GTK_CHECK_TYPE(obj, gnc_frequency_get_type())
/**
* A GNCFrequency is a VBox containing a scrollable GtkNotebook which
* allows the user to specify the frequency [of a scheduled
* transaction or budgeting category, for instance], manipulating a
* FreqSpec object in the process.
**/
typedef struct _GNCFrequency {
GtkVBox widget;
GtkVBox *vb;
GtkNotebook *nb;
GtkOptionMenu *freqOpt;
GnomeDateEdit *startDate;
GladeXML *gxml;
} GNCFrequency;
typedef struct _GNCFrequencyClass {
GtkVBoxClass parent_class;
void (*value_changed) (GNCFrequency *gf);
} GNCFrequencyClass;
struct pageDataTuple {
int idx;
UIFreqType uiFTVal;
char *name;
};
guint gnc_frequency_get_type( void );
/**
* For the default freq spec widget, use 'NULL'.
**/
GtkWidget * gnc_frequency_new( FreqSpec *fs );
void gnc_frequency_init( GNCFrequency *gf );
/**
* Sets up the given GNCFrequency with the given FreqSpec and
* UIFreqSpec. If the FreqSpec is NULL, then the default value is
* Daily; if the UIFreqSpec is not NONE, then that value is the
* default. If the FreqSpec is non-NULL, then it really should agree
* with the UIFreqSpec; this is considered a 'critical' error.
**/
void gnc_frequency_setup( GNCFrequency *gf, FreqSpec *fs );
/**
* Saves the state of the GNCFrequenecy widget into the given FreqSpec
* and UIFreqSpec.
* Places the start date in outStartDate, if it's not null.
**/
void gnc_frequency_save_state( GNCFrequency *gf, FreqSpec *fs, GDate *outStartDate );
END_GNOME_DECLS
#endif // !defined( GNC_FREQUENCY_H )

View File

@@ -207,7 +207,7 @@ gnc_verify_dialog_parented(gncUIWidget parent, const char *message,
if (parent != NULL)
gnome_dialog_set_parent(GNOME_DIALOG(verify_box), GTK_WINDOW(parent));
gnome_dialog_set_default(GNOME_DIALOG(verify_box), yes_is_default ? 0 : 1);
gnome_dialog_set_default(GNOME_DIALOG(verify_box), (yes_is_default ? 0 : 1));
return (gnome_dialog_run_and_close(GNOME_DIALOG(verify_box)) == 0);
}

View File

@@ -23,6 +23,9 @@
#ifndef __QUERY_USER_H__
#define __QUERY_USER_H__
// +jsled: for line below.
#include "gnc-ui.h"
void gnc_info_dialog(const char *message);
void gnc_info_dialog_parented(GtkWindow *parent, const char *message);
@@ -30,4 +33,7 @@ void gnc_warning_dialog(const char *message);
void gnc_error_dialog_parented(GtkWindow *parent, const char *message);
// +jsled: export so dialog-scheduledxaction can see it...
gboolean gnc_verify_dialog_parented(gncUIWidget parent, const char *message, gboolean yes_is_default);
#endif

View File

@@ -51,6 +51,8 @@
#include "dialog-totd.h"
#include "dialog-transfer.h"
#include "dialog-utils.h"
#include "dialog-scheduledxaction.h"
#include "dialog-nextrun.h"
#include "window-help.h"
#include "window-main.h"
@@ -880,6 +882,18 @@ gnc_main_window_fincalc_cb(GtkWidget *widget, gpointer data) {
gnc_ui_fincalc_dialog_create();
}
static void
gnc_ui_mainWindow_scheduled_xaction_cb(GtkWidget *widget, gpointer data)
{
gnc_ui_scheduled_xaction_dialog_create();
}
static void
gnc_ui_mainWindow_nextrun_cb( GtkWidget *widget, gpointer d )
{
gnc_ui_nextrun_dialog_create();
}
static void
gnc_main_window_gl_cb(GtkWidget *widget, gpointer data) {
xaccLedgerDisplay *ld;
@@ -1100,12 +1114,18 @@ gnc_main_window_create_menus(GNCMainInfo * maininfo) {
GNOMEUIINFO_END
};
static GnomeUIInfo gnc_developer_menu_template[] =
{
GNOMEUIINFO_END
};
static GnomeUIInfo gnc_main_menu_template[] =
{
GNOMEUIINFO_MENU_FILE_TREE(gnc_file_menu_template),
GNOMEUIINFO_SUBTREE(N_("_Tools"), gnc_tools_menu_template),
GNOMEUIINFO_SUBTREE(N_("_Settings"), gnc_settings_menu_template),
GNOMEUIINFO_SUBTREE(N_("_Windows"), gnc_windows_menu_template),
// GNOEMUIINFO_SUBTREE(N_("_Developer Options"), gnc_developer_menu_template),
GNOMEUIINFO_MENU_HELP_TREE(gnc_help_menu_template),
GNOMEUIINFO_END
};

View File

@@ -1058,7 +1058,6 @@ gnc_register_create_status_bar (RegWindow *regData)
return statusbar;
}
void
gnc_register_jump_to_blank (RegWindow *regData)
{

View File

@@ -0,0 +1,86 @@
/********************************************************************\
* formulacell.h -- Formula entry/handling/display cell *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
/**
* FILE:
* formulacell.h
*
* FUNCTION:
*
* The FormulaCell object implements a cell which can contain a
* mathematical formula involving some number of variables, defined by
* the user. Any standard math functions [*, /, +, -, ()] are
* supported, and any non-numeric, non-syntactic characters or
* character strings are interpreted as variables; these character
* strings may be enclosed by square brackets ([...]) if they contain
* spaces.
*
* The FormulaCell will check for formula validity [paren-matching,
* mainly].
*
* The FormulaCell...
*
* DOES...
*
* SHOULD DO...
* . numeric-value detection
* . paren-matching
* . quick-fill on variables
* . functions [special character strings which reference an external
* function
* . scheme
*
* HISTORY:
* Copyright (c) 2001 Joshua Sled <jsled@asynchronous.org>
**/
#ifndef __FORMULA_CELL_C__
#define __FORMULA_CELL_C__
#include <time.h>
#include "basiccell.h"
#include "date.h"
typedef struct _FormulaCell
{
BasicCell cell;
} FormulaCell;
/* installs a callback to handle date recording */
FormulaCell * xaccMallocFormulaCell (void);
void xaccDestroyFormulaCell (FormulaCell *cell);
/* days are 1-31, mon is 1-12, year 1900 == 1900 */
void xaccSetFormulaCellValue (FormulaCell *cell, int day, int mon, int year);
void xaccSetFormulaCellValueSecs (FormulaCell *cell, time_t secs);
void xaccSetFormulaCellValueSecsL (FormulaCell *cell, long long secs);
void xaccCommitFormulaCell (FormulaCell *cell);
void xaccFormulaCellGetDate (FormulaCell *cell, Timespec *ts);
#endif /* __FORMULA_CELL_C__ */
/* --------------- end of file ---------------------- */

View File

@@ -80,6 +80,8 @@ struct _SplitRegisterBuffer
CellBuffer sharesCell;
CellBuffer mxfrmCell;
CellBuffer notesCell;
CellBuffer formCreditCell;
CellBuffer formDebitCell;
};
typedef struct
@@ -110,6 +112,8 @@ static sample_string cell_sample_strings[] =
{ N_("sample:999,999.000"), 7}, /* tshrbaln cell */
{ N_("sample:999,999.000"), 7}, /* tbalance cell */
{ N_("sample:Notes field sample text string"), 7}, /* notes cell */
{ N_("sample:(x + 0.33 * y + (x+y) )"), 7 }, /* formula credit cell */
{ N_("sample:(x + 0.33 * y + (x+y) )"), 7 }, /* formula debit cell */
};
static CellAlignment cell_alignments[] =
@@ -134,6 +138,8 @@ static CellAlignment cell_alignments[] =
CELL_ALIGN_RIGHT, /* tshrbaln cell */
CELL_ALIGN_RIGHT, /* tbalance cell */
CELL_ALIGN_LEFT, /* notes cell */
CELL_ALIGN_LEFT, /* formula credit cell */
CELL_ALIGN_LEFT, /* formula debit cell */
};
@@ -145,7 +151,8 @@ xaccInitSplitRegister (SplitRegister *reg,
TableView *view,
VirtCellDataAllocator allocator,
VirtCellDataDeallocator deallocator,
VirtCellDataCopy copy);
VirtCellDataCopy copy,
gboolean templateMode);
/* ============================================== */
@@ -240,6 +247,7 @@ configAction (SplitRegister *reg)
xaccAddComboCellMenuItem (reg->actionCell, _("Dist"));
xaccAddComboCellMenuItem (reg->actionCell, _("Split"));
break;
default:
xaccAddComboCellMenuItem (reg->actionCell, _("Buy"));
xaccAddComboCellMenuItem (reg->actionCell, _("Sell"));
@@ -322,6 +330,8 @@ configLayout (SplitRegister *reg)
CellBlock *curs;
int i;
printf( "configLayout with type %d\n", reg->type );
/* fill things up with null cells */
for (i = 0; i < reg->cursor_header->num_cols; i++)
{
@@ -369,8 +379,13 @@ configLayout (SplitRegister *reg)
set_cell (reg, curs, DESC_CELL, 0, 2);
set_cell (reg, curs, MXFRM_CELL, 0, 3);
set_cell (reg, curs, RECN_CELL, 0, 4);
if ( reg->template ) {
set_cell( reg, curs, FDEBT_CELL, 0, 5);
set_cell( reg, curs, FCRED_CELL, 0, 6);
} else {
set_cell (reg, curs, DEBT_CELL, 0, 5);
set_cell (reg, curs, CRED_CELL, 0, 6);
}
set_cell (reg, curs, BALN_CELL, 0, 7);
curs = reg->cursor_ledger_double;
@@ -397,8 +412,13 @@ configLayout (SplitRegister *reg)
set_cell (reg, curs, MEMO_CELL, 0, 2);
set_cell (reg, curs, XFRM_CELL, 0, 3);
set_cell (reg, curs, RECN_CELL, 0, 4);
if ( reg->template ) {
set_cell( reg, curs, FDEBT_CELL, 0, 5);
set_cell( reg, curs, FCRED_CELL, 0, 6);
} else {
set_cell (reg, curs, DEBT_CELL, 0, 5);
set_cell (reg, curs, CRED_CELL, 0, 6);
}
break;
}
@@ -414,8 +434,13 @@ configLayout (SplitRegister *reg)
set_cell (reg, curs, DESC_CELL, 0, 2);
set_cell (reg, curs, MXFRM_CELL, 0, 3);
set_cell (reg, curs, RECN_CELL, 0, 4);
if ( reg->template ) {
set_cell (reg, curs, FDEBT_CELL, 0, 5);
set_cell (reg, curs, FCRED_CELL, 0, 6);
} else {
set_cell (reg, curs, DEBT_CELL, 0, 5);
set_cell (reg, curs, CRED_CELL, 0, 6);
}
curs = reg->cursor_ledger_double;
copy_cursor_row (reg, curs, reg->cursor_ledger_single, 0);
@@ -440,8 +465,13 @@ configLayout (SplitRegister *reg)
set_cell (reg, curs, MEMO_CELL, 0, 2);
set_cell (reg, curs, XFRM_CELL, 0, 3);
set_cell (reg, curs, RECN_CELL, 0, 4);
if ( reg->template ) {
set_cell (reg, curs, FDEBT_CELL, 0, 5);
set_cell (reg, curs, FCRED_CELL, 0, 6);
} else {
set_cell (reg, curs, DEBT_CELL, 0, 5);
set_cell (reg, curs, CRED_CELL, 0, 6);
}
break;
}
@@ -540,7 +570,6 @@ configLayout (SplitRegister *reg)
break;
}
/* --------------------------------------------------------- */
default:
PERR ("unknown register type %d \n", reg->type);
@@ -557,7 +586,8 @@ xaccMallocSplitRegister (SplitRegisterType type,
TableView *view,
VirtCellDataAllocator allocator,
VirtCellDataDeallocator deallocator,
VirtCellDataCopy copy)
VirtCellDataCopy copy,
gboolean templateMode)
{
SplitRegister * reg;
@@ -577,7 +607,8 @@ xaccMallocSplitRegister (SplitRegisterType type,
view,
allocator,
deallocator,
copy);
copy,
templateMode);
return reg;
}
@@ -655,7 +686,8 @@ xaccInitSplitRegister (SplitRegister *reg,
TableView *view,
VirtCellDataAllocator allocator,
VirtCellDataDeallocator deallocator,
VirtCellDataCopy copy)
VirtCellDataCopy copy,
gboolean templateMode)
{
Table * table;
@@ -666,6 +698,7 @@ xaccInitSplitRegister (SplitRegister *reg,
reg->type = type;
reg->style = style;
reg->use_double_line = use_double_line;
reg->template = templateMode;
/* --------------------------- */
/* define the number of columns in the display, malloc the cursors */
@@ -705,6 +738,8 @@ xaccInitSplitRegister (SplitRegister *reg,
NEW (TSHRBALN, tshrbaln, Price);
NEW (TBALN, tbalance, Price);
NEW (NOTES, notes, QuickFill);
NEW (FCRED, formCredit, QuickFill);
NEW (FDEBT, formDebit, QuickFill);
/* --------------------------- */
@@ -761,6 +796,11 @@ xaccInitSplitRegister (SplitRegister *reg,
/* the notes cell */
xaccSetBasicCellBlankHelp (&reg->notesCell->cell,
_("Enter notes for the transaction"));
/* the formula cell */
xaccSetBasicCellBlankHelp( &reg->formCreditCell->cell,
_("Enter credit formula for real transaction"));
xaccSetBasicCellBlankHelp( &reg->formDebitCell->cell,
_("Enter debit formula for real transaction"));
/* Use 6 decimal places for prices */
xaccSetPriceCellFraction (reg->priceCell, 1000000);
@@ -918,6 +958,8 @@ xaccDestroySplitRegister (SplitRegister *reg)
xaccDestroyPriceCell (reg->tshrbalnCell);
xaccDestroyPriceCell (reg->tbalanceCell);
xaccDestroyQuickFillCell (reg->notesCell);
xaccDestroyQuickFillCell (reg->formCreditCell);
xaccDestroyQuickFillCell (reg->formDebitCell);
reg->nullCell = NULL;
reg->dateCell = NULL;
@@ -940,6 +982,8 @@ xaccDestroySplitRegister (SplitRegister *reg)
reg->tshrbalnCell = NULL;
reg->tbalanceCell = NULL;
reg->notesCell = NULL;
reg->formCreditCell = NULL;
reg->formDebitCell = NULL;
for (i = 0; i < CELL_TYPE_COUNT; i++)
{
@@ -987,6 +1031,9 @@ xaccSplitRegisterGetChangeFlag (SplitRegister *reg)
changed |= MOD_MXFRM & reg->mxfrmCell->cell.changed;
changed |= MOD_NOTES & reg->notesCell->cell.changed;
changed |= MOD_AMNT & reg->formCreditCell->cell.changed;
changed |= MOD_AMNT & reg->formDebitCell->cell.changed;
return changed;
}
@@ -1010,6 +1057,9 @@ xaccSplitRegisterGetConditionalChangeFlag (SplitRegister *reg)
changed |= MOD_MXFRM & reg->mxfrmCell->cell.conditionally_changed;
changed |= MOD_NOTES & reg->notesCell->cell.conditionally_changed;
changed |= MOD_AMNT & reg->formCreditCell->cell.conditionally_changed;
changed |= MOD_AMNT & reg->formDebitCell->cell.conditionally_changed;
return changed;
}
@@ -1031,6 +1081,8 @@ xaccSplitRegisterClearChangeFlag (SplitRegister *reg)
reg->sharesCell->cell.changed = 0;
reg->mxfrmCell->cell.changed = 0;
reg->notesCell->cell.changed = 0;
reg->formDebitCell->cell.changed = 0;
reg->formCreditCell->cell.changed = 0;
}
/* ============================================== */
@@ -1309,6 +1361,8 @@ xaccDestroySplitRegisterBuffer (SplitRegisterBuffer *srb)
destroyCellBuffer(&srb->sharesCell);
destroyCellBuffer(&srb->mxfrmCell);
destroyCellBuffer(&srb->notesCell);
destroyCellBuffer(&srb->formCreditCell);
destroyCellBuffer(&srb->formDebitCell);
g_free(srb);
}
@@ -1349,6 +1403,8 @@ xaccSplitRegisterSaveCursor(SplitRegister *sr, SplitRegisterBuffer *srb)
saveCell(&sr->sharesCell->cell, &srb->sharesCell);
saveCell(&sr->mxfrmCell->cell, &srb->mxfrmCell);
saveCell(&sr->notesCell->cell, &srb->notesCell);
saveCell(&sr->formCreditCell->cell, &srb->formCreditCell);
saveCell(&sr->formDebitCell->cell, &srb->formDebitCell);
}
/* ============================================== */
@@ -1412,6 +1468,8 @@ xaccSplitRegisterRestoreCursorChanged(SplitRegister *sr,
restoreCellChanged(&sr->sharesCell->cell, &srb->sharesCell, cursor);
restoreCellChanged(&sr->mxfrmCell->cell, &srb->mxfrmCell, cursor);
restoreCellChanged(&sr->notesCell->cell, &srb->notesCell, cursor);
restoreCellChanged(&sr->formCreditCell->cell, &srb->formCreditCell, cursor);
restoreCellChanged(&sr->formDebitCell->cell, &srb->formDebitCell, cursor);
}
/* keep in sync with CellType enum */
@@ -1435,7 +1493,9 @@ static const char *cell_names[] =
"trans-debit",
"trans-share-balance",
"trans-balance",
"notes"
"notes",
"credit formula",
"debit formula",
};
const char *

View File

@@ -75,6 +75,7 @@ typedef enum
INCOME_LEDGER,
PORTFOLIO_LEDGER,
SEARCH_LEDGER,
NUM_REGISTER_TYPES
} SplitRegisterType;
@@ -103,7 +104,9 @@ typedef enum
TSHRBALN_CELL,
TBALN_CELL,
NOTES_CELL,
CELL_TYPE_COUNT
FCRED_CELL, // formula credit, used by the template ledger
FDEBT_CELL, // formula debit, used by the template ledger
CELL_TYPE_COUNT,
} CellType;
/*
@@ -139,6 +142,8 @@ typedef enum
MOD_PRIC = 1 << 9,
MOD_SHRS = 1 << 10,
MOD_NOTES = 1 << 11,
MOD_FCRED = 1 << 12,
MOD_FDEBT = 1 << 13,
MOD_ALL = 0xffff
} CellModifiedFlags;
@@ -204,6 +209,9 @@ struct _SplitRegister
PriceCell * tbalanceCell;
QuickFillCell * notesCell;
QuickFillCell * formCreditCell;
QuickFillCell * formDebitCell;
SplitRegisterType type;
SplitRegisterStyle style;
gboolean use_double_line;
@@ -213,6 +221,17 @@ struct _SplitRegister
BasicCell *header_cells[CELL_TYPE_COUNT];
BasicCell *cells[CELL_TYPE_COUNT];
/**
* A flag indicating a "template" register.
**/
gboolean template;
/**
* The template account which the transactions in a template
* splitregister will belong to.
**/
Account *templateAcct;
/* user_data allows users of this object to hang
* private data onto it */
gpointer user_data;
@@ -236,7 +255,8 @@ xaccMallocSplitRegister (SplitRegisterType type,
TableView *view,
VirtCellDataAllocator allocator,
VirtCellDataDeallocator deallocator,
VirtCellDataCopy copy);
VirtCellDataCopy copy,
gboolean templateMode);
void xaccConfigSplitRegister (SplitRegister *reg,
SplitRegisterType type,

View File

@@ -66,6 +66,20 @@
; (list "Extensions" "")
; (lambda () (gnc:main-win-account-group-write win))))
(define schedxact-editor-item
(gnc:make-menu-item (N_ "SchedXact: Editor")
(N_ "Editor of Scheduled Transactions")
(list "Extensions" "")
(lambda () (gnc:sx-editor)))
)
(define schedxact-slr-item
(gnc:make-menu-item (N_ "SchedXact: Since Last Run")
(N_ "since last run dialog")
(list "Extensions" "")
(lambda () (gnc:sx-since-last-run)))
)
(define progress-item
(gnc:make-menu-item (N_ "Test progress dialog")
(N_ "Test progress dialog")
@@ -91,7 +105,14 @@
(gnc:add-extension menu)
; (gnc:add-extension export-item)
(gnc:add-extension progress-item))
;; NOTE: this is the inverse order from how you may want them to
;; appear in the menu [prepending to some list]...
(gnc:add-extension progress-item)
(gnc:add-extension schedxact-slr-item)
(gnc:add-extension schedxact-editor-item)
)
(if (gnc:debugging?)
(gnc:hook-add-dangler gnc:*ui-startup-hook*