diff --git a/src/MultiLedger.c b/src/MultiLedger.c index 442b9bece7..8c8d38f877 100644 --- a/src/MultiLedger.c +++ b/src/MultiLedger.c @@ -33,6 +33,7 @@ #include "Transaction.h" #include "util.h" + /** GLOBALS *********************************************************/ /* These are globals because they describe the state of the entire session. * The is, there must be only one instance of these per GUI session. @@ -209,7 +210,7 @@ xaccLedgerDisplaySimple (Account *acc) xaccLedgerDisplay * xaccLedgerDisplayAccGroup (Account *acc) - { +{ xaccLedgerDisplay *retval; Account **list; int ledger_type; @@ -271,7 +272,7 @@ xaccLedgerDisplayAccGroup (Account *acc) if (list) _free (list); return retval; - } +} static gncUIWidget xaccLedgerDisplayParent(void *user_data) @@ -287,6 +288,20 @@ xaccLedgerDisplayParent(void *user_data) return (regData->get_parent)(regData); } +static void +xaccLedgerDisplaySetHelp(void *user_data, const char *help_str) +{ + xaccLedgerDisplay *regData = user_data; + + if (regData == NULL) + return; + + if (regData->set_help == NULL) + return; + + (regData->set_help)(regData, help_str); +} + /********************************************************************\ * xaccLedgerDisplayLedger * * opens up a ledger window for a list of accounts * @@ -300,8 +315,8 @@ xaccLedgerDisplayParent(void *user_data) xaccLedgerDisplay * xaccLedgerDisplayGeneral (Account *lead_acc, Account **acclist, int ledger_type) - { - xaccLedgerDisplay *regData = NULL; +{ + xaccLedgerDisplay *regData = NULL; /******************************************************************\ \******************************************************************/ @@ -340,6 +355,7 @@ xaccLedgerDisplayGeneral (Account *lead_acc, Account **acclist, int ledger_type) regData->redraw = NULL; regData->destroy = NULL; regData->get_parent = NULL; + regData->set_help = NULL; regData->gui_hook = NULL; regData->dirty = 0; regData->balance = 0.0; @@ -373,7 +389,9 @@ xaccLedgerDisplayGeneral (Account *lead_acc, Account **acclist, int ledger_type) * but will not do the gui init */ regData->ledger = xaccMallocSplitRegister (ledger_type); - xaccSRSetData(regData->ledger, regData, xaccLedgerDisplayParent); + xaccSRSetData(regData->ledger, regData, + xaccLedgerDisplayParent, + xaccLedgerDisplaySetHelp); regData->dirty = 1; xaccLedgerDisplayRefresh (regData); diff --git a/src/MultiLedger.h b/src/MultiLedger.h index 2542b2b2fe..403214adea 100644 --- a/src/MultiLedger.h +++ b/src/MultiLedger.h @@ -32,6 +32,7 @@ #include "SplitLedger.h" #include "Transaction.h" + /* the MAX_QUERY_SPLITS define determines how many transactions should be shown * in the register. Its set to a default of 30. But this should be converted * into a user-configurable value. So hack-alert on the configuration aspect. @@ -72,6 +73,7 @@ struct _xaccLedgerDisplay { void (*redraw) (xaccLedgerDisplay *); /* redraw callback */ void (*destroy) (xaccLedgerDisplay *); /* destroy callback */ gncUIWidget (*get_parent) (xaccLedgerDisplay *); /* get parent widget */ + void (*set_help) (xaccLedgerDisplay *, const char *); /* help string */ }; diff --git a/src/Refresh.c b/src/Refresh.c index 3915bca4a7..410ee926a7 100644 --- a/src/Refresh.c +++ b/src/Refresh.c @@ -126,6 +126,9 @@ gnc_transaction_ui_refresh(Transaction *trans) Split *split; int i, num_splits; + if (trans == NULL) + return; + xaccTransDisplayRefresh(trans); num_splits = xaccTransCountSplits(trans); diff --git a/src/SplitLedger.c b/src/SplitLedger.c index 8762ace1d7..37a4813a7b 100644 --- a/src/SplitLedger.c +++ b/src/SplitLedger.c @@ -127,7 +127,10 @@ struct _SRInfo void *user_data; /* hook to get parent widget */ - gncUIWidget (*get_parent) (void * user_data); + SRGetParentCallback get_parent; + + /* hook to set help string */ + SRSetHelpCallback set_help; }; @@ -156,6 +159,8 @@ static int force_double_entry_awareness = 0; /* This static indicates the debugging module that this .o belongs to. */ static short module = MOD_LEDGER; +/* The character used to separate accounts. */ +static char account_separator = ':'; /* static prototypes */ static Transaction * xaccSRGetTrans (SplitRegister *reg, @@ -220,7 +225,8 @@ xaccSRGetParent(SplitRegister *reg) void xaccSRSetData(SplitRegister *reg, void *user_data, - SRGetParentCallback get_parent) + SRGetParentCallback get_parent, + SRSetHelpCallback set_help) { SRInfo *info = xaccSRGetInfo(reg); @@ -228,6 +234,13 @@ xaccSRSetData(SplitRegister *reg, void *user_data, info->user_data = user_data; info->get_parent = get_parent; + info->set_help = set_help; +} + +void +xaccSRSetAccountSeparator(char separator) +{ + account_separator = separator; } /* ======================================================== */ @@ -387,6 +400,20 @@ LedgerTraverse (Table *table, /* ======================================================== */ +static void +LedgerSetHelp (Table *table, const char *help_str, void *client_data) +{ + SplitRegister *reg = client_data; + SRInfo *info = xaccSRGetInfo(reg); + + if (info->set_help == NULL) + return; + + info->set_help(info->user_data, help_str); +} + +/* ======================================================== */ + static void LedgerDestroy (SplitRegister *reg) { @@ -484,10 +511,8 @@ xaccSRGetCurrentTrans (SplitRegister *reg) } split = (Split *) reg->table->user_data[vr][vc]; - if (split == NULL) { - PERR ("Internal Error: xaccSRGetCurrentTrans(): no parent \n"); + if (split == NULL) return NULL; - } return xaccSplitGetParent(split); } @@ -961,8 +986,8 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) } if (MOD_MEMO & changed) { - DEBUG ("xaccSRSaveRegEntry(): MOD_MEMO: %s\n", reg->memoCell->value); - xaccSplitSetMemo (split, reg->memoCell->value); + DEBUG ("xaccSRSaveRegEntry(): MOD_MEMO: %s\n", reg->memoCell->cell.value); + xaccSplitSetMemo (split, reg->memoCell->cell.value); } /* -------------------------------------------------------------- */ @@ -980,12 +1005,17 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) /* do some reparenting. Insertion into new account will automatically * delete this split from the old account */ old_acc = xaccSplitGetAccount (split); - new_acc = xaccGetAccountByName (trans, reg->xfrmCell->cell.value); - xaccAccountInsertSplit (new_acc, split); - - /* make sure any open windows of the old account get redrawn */ - gnc_account_ui_refresh(old_acc); - gnc_refresh_main_window(); + new_acc = xaccGetAccountByFullName (trans, reg->xfrmCell->cell.value, + account_separator); + + if (old_acc != new_acc) + { + xaccAccountInsertSplit (new_acc, split); + + /* make sure any open windows of the old account get redrawn */ + gnc_account_ui_refresh(old_acc); + gnc_refresh_main_window(); + } } if (MOD_MXFRM & changed) { @@ -1023,12 +1053,17 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) /* do some reparenting. Insertion into new account will automatically * delete from the old account */ old_acc = xaccSplitGetAccount (other_split); - new_acc = xaccGetAccountByName (trans, reg->mxfrmCell->cell.value); - xaccAccountInsertSplit (new_acc, other_split); - - /* make sure any open windows of the old account get redrawn */ - gnc_account_ui_refresh(old_acc); - gnc_refresh_main_window(); + new_acc = xaccGetAccountByFullName (trans, reg->mxfrmCell->cell.value, + account_separator); + + if (old_acc != new_acc) + { + xaccAccountInsertSplit (new_acc, other_split); + + /* make sure any open windows of the old account get redrawn */ + gnc_account_ui_refresh(old_acc); + gnc_refresh_main_window(); + } } } @@ -1043,10 +1078,17 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) */ if ((MOD_AMNT | MOD_NAMNT) & changed) { double new_amount; + double credit; + double debit; + if (MOD_AMNT & changed) { - new_amount = (reg->creditCell->amount) - (reg->debitCell->amount); + credit = xaccGetPriceCellValue(reg->creditCell); + debit = xaccGetPriceCellValue(reg->debitCell); + new_amount = credit - debit; } else { - new_amount = (reg->ndebitCell->amount) - (reg->ncreditCell->amount); + credit = xaccGetPriceCellValue(reg->ncreditCell); + debit = xaccGetPriceCellValue(reg->ndebitCell); + new_amount = debit - credit; } DEBUG ("xaccSRSaveRegEntry(): MOD_AMNT: %f\n", new_amount); if ((EQUITY_REGISTER == (reg->type & REG_TYPE_MASK)) || @@ -1062,9 +1104,13 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) if (MOD_PRIC & changed) { Account *acc; + double price; int n; - DEBUG ("xaccSRSaveRegEntry(): MOD_PRIC: %f\n", reg->priceCell->amount); - xaccSplitSetSharePrice (split, reg->priceCell->amount); + + price = xaccGetPriceCellValue(reg->priceCell); + + DEBUG ("xaccSRSaveRegEntry(): MOD_PRIC: %f\n", price); + xaccSplitSetSharePrice (split, price); /* Here we handle a very special case: the user just created * an account, which now has two splits in it, and the user @@ -1089,7 +1135,7 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) if (DEQ (currprice, 1.0)) { t = xaccSplitGetParent (s); xaccTransBeginEdit (t, 0); - xaccSplitSetSharePrice (s, reg->priceCell->amount); + xaccSplitSetSharePrice (s, price); xaccTransCommitEdit (t); } } @@ -1097,8 +1143,10 @@ xaccSRSaveRegEntry (SplitRegister *reg, Transaction *newtrans) } if (MOD_VALU & changed) { - DEBUG ("xaccSRSaveRegEntry(): MOD_VALU: %f\n", reg->valueCell->amount); - xaccSplitSetValue (split, (reg->valueCell->amount)); + double value = xaccGetPriceCellValue(reg->valueCell); + + DEBUG ("xaccSRSaveRegEntry(): MOD_VALU: %f\n", value); + xaccSplitSetValue (split, value); } PINFO ("xaccSRSaveRegEntry(): finished saving split %s of trans %s \n", @@ -1133,12 +1181,12 @@ xaccSRLoadTransEntry (SplitRegister *reg, Split *split, int do_commit) char buff[2]; double baln; int typo = reg->type & REG_TYPE_MASK; - /* int style = reg->type & REG_STYLE_MASK; */ /* don't even bother doing a load if there is no current cursor */ if (!(reg->table->current_cursor)) return; ENTER ("SRLoadTransEntry(): s=%p commit=%d\n", split, do_commit); + if (!split) { /* we interpret a NULL split as a blank split */ xaccSetDateCellValueSecs (reg->dateCell, 0); @@ -1149,7 +1197,7 @@ xaccSRLoadTransEntry (SplitRegister *reg, Split *split, int do_commit) xaccSetPriceCellValue (reg->balanceCell, 0.0); xaccSetComboCellValue (reg->actionCell, ""); - xaccSetBasicCellValue (reg->memoCell, ""); + xaccSetQuickFillCellValue (reg->memoCell, ""); xaccSetComboCellValue (reg->xfrmCell, ""); xaccSetComboCellValue (reg->mxfrmCell, ""); xaccSetDebCredCellValue (reg->debitCell, @@ -1198,12 +1246,19 @@ xaccSRLoadTransEntry (SplitRegister *reg, Split *split, int do_commit) * if there are exactly two splits. * xfrm is the "straight" display, "mxfrm" is the "mirrored" display. */ - accname = xaccAccountGetName (xaccSplitGetAccount (split)); + accname = xaccAccountGetFullName (xaccSplitGetAccount (split), + account_separator); xaccSetComboCellValue (reg->xfrmCell, accname); + free(accname); + { Split *s = xaccGetOtherSplit (split); + gncBoolean need_to_free = GNC_F; + if (s) { - accname = xaccAccountGetName (xaccSplitGetAccount (s)); + accname = xaccAccountGetFullName (xaccSplitGetAccount (s), + account_separator); + need_to_free = GNC_T; } else { /* determine whether s is null because threre are three * or more splits, or whether there is only one ... */ @@ -1215,9 +1270,11 @@ xaccSRLoadTransEntry (SplitRegister *reg, Split *split, int do_commit) } } xaccSetComboCellValue (reg->mxfrmCell, accname); + if (need_to_free) + free(accname); } - xaccSetBasicCellValue (reg->memoCell, xaccSplitGetMemo (split)); + xaccSetQuickFillCellValue (reg->memoCell, xaccSplitGetMemo (split)); buff[0] = xaccSplitGetReconcile (split); buff[1] = 0x0; @@ -1459,6 +1516,8 @@ xaccSRLoadRegister (SplitRegister *reg, Split **slist, CellBlock *lead_cursor; gncBoolean found_pending = GNC_F; + xaccSplitRegisterConfigColors (reg); + info->default_source_account = default_source_acc; table = reg->table; @@ -1546,8 +1605,9 @@ xaccSRLoadRegister (SplitRegister *reg, Split **slist, vrow ++; phys_row += reg->trans_cursor->numRows; - /* loop over all of the splits in the transaction */ - /* the do..while will automaticaly put a blank (null) split at the end */ + /* loop over all of the splits in the transaction. The + * do..while will automaticaly put a blank (null) split + * at the end. */ trans = xaccSplitGetParent (split); j = 0; do { @@ -1662,9 +1722,14 @@ xaccSRLoadRegister (SplitRegister *reg, Split **slist, xaccRefreshTableGUI (table); + /* set the completion character for the xfer cells */ + xaccComboCellSetCompleteChar (reg->mxfrmCell, account_separator); + xaccComboCellSetCompleteChar (reg->xfrmCell, account_separator); + /* enable callback for cursor user-driven moves */ table->move_cursor = LedgerMoveCursor; table->traverse = LedgerTraverse; + table->set_help = LedgerSetHelp; table->client_data = (void *) reg; } @@ -1677,6 +1742,7 @@ LoadXferCell (ComboCell *cell, char *base_currency, char *base_security) { Account * acc; + char *name; int n; ENTER ("LoadXferCell(): curr=%s secu=%s\n", base_currency, base_security); @@ -1698,19 +1764,26 @@ LoadXferCell (ComboCell *cell, if (secu && (0x0 == secu[0])) secu = 0x0; DEBUG ("LoadXferCell(): curr=%s secu=%s acct=%s\n", - curr, secu, xaccAccountGetName (acc)); + curr, secu, xaccAccountGetName (acc)); + if ( (!safe_strcmp(curr,base_currency)) || (!safe_strcmp(curr,base_security)) || (secu && (!safe_strcmp(secu,base_currency))) || (secu && (!safe_strcmp(secu,base_security))) ) { - xaccAddComboCellMenuItem (cell, xaccAccountGetName (acc)); + name = xaccAccountGetFullName (acc, account_separator); + if (name != NULL) + { + xaccAddComboCellMenuItem (cell, name); + free(name); + } } LoadXferCell (cell, xaccAccountGetChildren (acc), base_currency, base_security); n++; acc = xaccGroupGetAccount (grp, n); } + LEAVE ("LoadXferCell()\n"); } diff --git a/src/register/Makefile.in b/src/register/Makefile.in index a6e54147c0..17cda3f8aa 100644 --- a/src/register/Makefile.in +++ b/src/register/Makefile.in @@ -42,7 +42,7 @@ COMMON_SRCS := basiccell.c cellblock.c \ datecell.c pricecell.c QuickFill.c quickfillcell.c \ recncell.c splitreg.c \ table-allgui.c table-html.c textcell.c -MOTIF_SRCS := table-motif.c combocell-motif.c +MOTIF_SRCS := table-motif.c combocell-motif.c quickfillcell-motif.c GNOME_SRCS := table-gnome.c QT_SRCS := table-qt.cpp combocell-qt.cpp CLEAN_SUBDIRS := gnome diff --git a/src/register/QuickFill.c b/src/register/QuickFill.c index cd632033e4..17097ef047 100644 --- a/src/register/QuickFill.c +++ b/src/register/QuickFill.c @@ -110,23 +110,72 @@ xaccGetQuickFill( QuickFill *qf, char c ) /********************************************************************\ \********************************************************************/ QuickFill * -xaccGetQuickFillStr( QuickFill *qf, const char *str ) +xaccGetQuickFillStrLen( QuickFill *qf, const char *str, int len ) { if (str == NULL) return NULL; - while (*str != '\0') + while ((*str != '\0') && (len > 0)) { if (qf == NULL) return NULL; qf = qf->qf[CHAR_TO_INDEX(*str)]; str++; + len--; } return qf; } +/********************************************************************\ +\********************************************************************/ +QuickFill * +xaccGetQuickFillStr( QuickFill *qf, const char *str ) +{ + if (str == NULL) + return NULL; + + return xaccGetQuickFillStrLen(qf, str, strlen(str)); +} + +/********************************************************************\ +\********************************************************************/ +QuickFill * +xaccGetQuickFillUniqueLen( QuickFill *qf, int * length ) +{ + int last = 0; + int count; + int i; + + *length = 0; + + if (qf == NULL) + return NULL; + + while (1) + { + count = 0; + for( i=0; iqf[i] != NULL) + { + count++; + if (count > 1) + return qf; + + last = i; + } + } + + if (count == 0) + return qf; + + qf = qf->qf[last]; + (*length)++; + } +} + /********************************************************************\ \********************************************************************/ void diff --git a/src/register/QuickFill.h b/src/register/QuickFill.h index 015f9e385a..cf8a97ee7a 100644 --- a/src/register/QuickFill.h +++ b/src/register/QuickFill.h @@ -66,6 +66,8 @@ QuickFill *xaccMallocQuickFill( void ); void xaccFreeQuickFill( QuickFill *qf ); QuickFill *xaccGetQuickFill( QuickFill *qf, char c ); QuickFill *xaccGetQuickFillStr( QuickFill *qf, const char *str ); +QuickFill *xaccGetQuickFillStrLen( QuickFill *qf, const char *str, int len ); +QuickFill *xaccGetQuickFillUniqueLen( QuickFill *qf, int *len ); void xaccQFInsertText( QuickFill *qf, const char *text, QuickFillSort ); /** GLOBALS *********************************************************/ diff --git a/src/register/basiccell.c b/src/register/basiccell.c index 9e40d58fb6..1ba8376a7d 100644 --- a/src/register/basiccell.c +++ b/src/register/basiccell.c @@ -43,6 +43,20 @@ BasicCell * xaccMallocBasicCell (void) /* ===================================================== */ +static char * +BasicCellHelpValue(BasicCell *cell) +{ + if ((cell->value != NULL) && (cell->value[0] != 0)) + return strdup(cell->value); + + if (cell->blank_help != NULL) + return strdup(cell->blank_help); + + return NULL; +} + +/* ===================================================== */ + void xaccInitBasicCell (BasicCell *cell) { cell->input_output = XACC_CELL_ALLOW_ALL; @@ -50,15 +64,18 @@ void xaccInitBasicCell (BasicCell *cell) cell->fg_color = 0x0; /* black */ cell->use_bg_color = 0; /* ignore the color */ cell->use_fg_color = 0; /* ignore the color */ - cell->value = 0x0; + cell->value = NULL; + cell->blank_help = NULL; cell->changed = 0; cell->set_value = NULL; cell->enter_cell = NULL; cell->modify_verify = NULL; + cell->direct_update = NULL; cell->leave_cell = NULL; cell->realize = NULL; cell->move = NULL; cell->destroy = NULL; + cell->get_help_value = BasicCellHelpValue; cell->gui_private = NULL; } @@ -76,6 +93,10 @@ void xaccDestroyBasicCell (BasicCell *cell) free (cell->value); } + if (cell->blank_help) { + free (cell->blank_help); + } + /* help prevent access to freed memory */ xaccInitBasicCell (cell); @@ -106,4 +127,35 @@ void xaccSetBasicCellValue (BasicCell *cell, const char *val) } } +/* ===================================================== */ + +void +xaccSetBasicCellBlankHelp (BasicCell *cell, const char *blank_help) +{ + if (cell == NULL) + return; + + if (cell->blank_help != NULL) + free(cell->blank_help); + + if (blank_help == NULL) + cell->blank_help = NULL; + else + cell->blank_help = strdup(blank_help); +} + +/* ===================================================== */ + +char * +xaccBasicCellGetHelp (BasicCell *cell) +{ + if (cell == NULL) + return NULL; + + if (cell->get_help_value == NULL) + return NULL; + + return cell->get_help_value(cell); +} + /* ================== end of file ====================== */ diff --git a/src/register/basiccell.h b/src/register/basiccell.h index 565b8ed461..e643880c4d 100644 --- a/src/register/basiccell.h +++ b/src/register/basiccell.h @@ -114,6 +114,14 @@ * It must return a string, or void if it rejects the change. * The returned string will be used to update the cell value. * + * The direct_update() callback is called to pass raw gui data + * to the cell. The exact format of the data is determined + * by the gui. The callback should return TRUE if the event + * was handled, i.e., there is no need to call the modify + * update. If the value needs to be changed, the newval_ptr + * should be set to a malloc'd new value. The other arguments + * work as above. + * * Some memory management rules: * (1) the callback must not modify the values of old, change, new * (2) if the callback likes the new string, it may return the @@ -177,6 +185,8 @@ #ifndef __XACC_BASIC_CELL_H__ #define __XACC_BASIC_CELL_H__ +#include "gnc-common.h" + /* define a bitmask */ #define XACC_CELL_ALLOW_NONE 0x0 #define XACC_CELL_ALLOW_SHADOW 0x1 @@ -185,7 +195,6 @@ #define XACC_CELL_ALLOW_EXACT_ONLY 0x4 typedef struct _BasicCell BasicCell; -typedef unsigned int uint32; struct _BasicCell { @@ -200,6 +209,7 @@ struct _BasicCell { /* ==================================================== */ char * value; /* current value */ + char *blank_help; /* help when value is blank */ unsigned int changed; /* 2^32-1 if value modified */ char input_output; /* zero if output-only */ @@ -221,6 +231,13 @@ struct _BasicCell { int *cursor_position, int *start_selection, int *end_selection); + gncBoolean (*direct_update) (BasicCell *, + const char *oldval, + char **newval_ptr, + int *cursor_position, + int *start_selection, + int *end_selection, + void *gui_data); const char * (*leave_cell) (BasicCell *, const char * current); @@ -232,16 +249,20 @@ struct _BasicCell { int phys_row, int phys_col); void (* destroy) (BasicCell *); + char * (*get_help_value) (BasicCell *); + /* general hook for gui-private data */ void * gui_private; }; -BasicCell * xaccMallocBasicCell (void); +BasicCell * xaccMallocBasicCell (void); void xaccInitBasicCell (BasicCell *); void xaccDestroyBasicCell (BasicCell *); void xaccSetBasicCellValue (BasicCell *, const char *); +void xaccSetBasicCellBlankHelp (BasicCell *, const char *); +char * xaccBasicCellGetHelp (BasicCell *); #endif /* __XACC_BASIC_CELL_H__ */ /* ------------------ end of file ---------------------- */ diff --git a/src/register/cellblock.c b/src/register/cellblock.c index fdaf34bd53..876b64c2af 100644 --- a/src/register/cellblock.c +++ b/src/register/cellblock.c @@ -39,8 +39,9 @@ CellBlock * xaccMallocCellBlock (int numrows, int numcols) arr->numRows = 0; arr->numCols = 0; - arr->active_bg_color = 0xffffff; /* white */ - arr->passive_bg_color = 0xffffff; /* white */ + arr->active_bg_color = 0xffffff; /* white */ + arr->passive_bg_color = 0xffffff; /* white */ + arr->passive_bg_color2 = 0xffffff; /* white */ arr->user_data = NULL; arr->cells = NULL; diff --git a/src/register/cellblock.h b/src/register/cellblock.h index 4115c549ba..8e633f402a 100644 --- a/src/register/cellblock.h +++ b/src/register/cellblock.h @@ -88,10 +88,14 @@ struct _CellBlock { * the currently active cursor). * * The passive_bg_color is the default color for the cell background - * (in argb format) when the cell block is not highlighted. + * (in argb format) of the first row when the cell block is not highlighted. + * + * The passive_bg_color2 is the default color for cell backgrounds + * in other rows of the cellblock when it is not highlighted. */ uint32 active_bg_color; uint32 passive_bg_color; + uint32 passive_bg_color2; /* other attributes */ short *widths; /* column widths */ diff --git a/src/register/combocell-motif.c b/src/register/combocell-motif.c index ca3a01a30b..4d95d2510a 100644 --- a/src/register/combocell-motif.c +++ b/src/register/combocell-motif.c @@ -583,4 +583,25 @@ void xaccComboCellSetStrict (ComboCell *cell, gncBoolean strict) { } +/* =============================================== */ + +void +xaccComboCellSetCompleteChar (ComboCell *cell, char complete_char) +{ +} + +/* =============================================== */ + +void +xaccComboCellSetIgnoreString (ComboCell *cell, const char *ignore_string) +{ +} + +/* =============================================== */ + +void +xaccComboCellSetIgnoreHelp (ComboCell *cell, const char *ignore_help) +{ +} + /* =============== end of file =================== */ diff --git a/src/register/combocell.h b/src/register/combocell.h index 51aa73d739..b2a52ecffd 100644 --- a/src/register/combocell.h +++ b/src/register/combocell.h @@ -44,7 +44,7 @@ typedef struct _ComboCell { BasicCell cell; - char ** menuitems; /* not used in the gnome version */ + char ** menuitems; /* not used in the gnome version */ } ComboCell; ComboCell * xaccMallocComboCell (void); @@ -61,6 +61,21 @@ void xaccAddComboCellMenuItem (ComboCell *, char * menustr); * to strict, i.e., only menu items are accepted. */ void xaccComboCellSetStrict (ComboCell *, gncBoolean); +/* Only functional in Gnome, right now. Sets a character used + * for special completion processing. */ +void xaccComboCellSetCompleteChar (ComboCell *, char); + +/* Only functional in Gnome, right now. Sets a string which, + * if the cell has that value, will be returned on an enter, + * thus preventing the cell from being edited. This is used + * for transactions with multiple splits. */ +void xaccComboCellSetIgnoreString (ComboCell *, const char *); + +/* Only functional in Gnome, right now. Sets a string which, + * if the cell has the ignore value, will be returned as the + * help string. */ +void xaccComboCellSetIgnoreHelp (ComboCell *, const char *); + #endif /* __XACC_COMBO_CELL_H__ */ /* --------------- end of file ---------------------- */ diff --git a/src/register/datecell.c b/src/register/datecell.c index e1b771e75d..66f244ed5e 100644 --- a/src/register/datecell.c +++ b/src/register/datecell.c @@ -54,8 +54,8 @@ static short module = MOD_REGISTER; /* ================================================ */ -static -void xaccParseDate (struct tm *parsed, const char * datestr) +static void +xaccParseDate (struct tm *parsed, const char * datestr) { int iday, imonth, iyear; if (!parsed) return; @@ -121,6 +121,43 @@ xaccValidateDate (struct tm *date, int recur) } +/* ================================================ */ + +static char * +DateCellHelpValue(BasicCell *bcell) +{ + DateCell *cell = (DateCell *) bcell; + + if ((bcell->value != NULL) && (bcell->value[0] != 0)) + { + char string[1024]; + struct tm time; + + memset(&time, 0, sizeof(time)); + + if (bcell->value != NULL) + xaccParseDate (&time, bcell->value); + else + { + time.tm_mday = cell->date.tm_mday; + time.tm_mon = cell->date.tm_mon; + time.tm_year = cell->date.tm_year; + } + + xaccValidateDate(&time, GNC_F); + mktime(&time); + + strftime(string, sizeof(string), "%A %d %B %Y", &time); + + return strdup(string); + } + + if (bcell->blank_help != NULL) + return strdup(bcell->blank_help); + + return NULL; +} + /* ================================================ */ static const char * @@ -150,32 +187,56 @@ DateMV (BasicCell *_cell, int *end_selection) { DateCell *cell = (DateCell *) _cell; + gncBoolean accept = GNC_F; + gncBoolean accel = GNC_F; struct tm *date; char buff[30]; char *datestr; - int accel=0; - short accept=0; /* if user hit backspace, accept the change */ - if (!change) accept=1; - else if (0x0 == change[0]) accept=1; + if (change == NULL) accept = GNC_T; + else if (0x0 == change[0]) accept = GNC_T; + else + { + int i, count = 0; + char separator = dateSeparator(); + gncBoolean ok = GNC_T; - /* accept any numeric input */ - else if (isdigit (change[0])) accept=1; + for (i=0; 0 != change[i]; i++) + { + /* accept only numbers or a date separator. Note that the + * separator of '-' (for DATE_FORMAT_ISO) takes precedence + * over the accelerator below! */ + if (!isdigit(change[i]) && (separator != change[i])) + ok = GNC_F; - /* accept the separator character */ - /* Note that the separator of '-' (for DATE_FORMAT_ISO) takes precedence - over the accelerator below! */ - else if (dateSeparator() == change[0]) accept=1; + if (separator == change[i]) + count++; + } + + for (i=0; 0 != oldval[i]; i++) + if (separator == oldval[i]) + count++; + + if (2 < count) + ok = GNC_F; + + if (ok) + accept = GNC_T; + } /* keep a copy of the new value */ if (accept) { if (cell->cell.value) free (cell->cell.value); cell->cell.value = strdup (newval); + xaccParseDate (&(cell->date), newval); return newval; } /* otherwise, maybe its an accelerator key. */ + if (strlen(change) != 1) + return NULL; + date = &(cell->date); /* handle accelerator keys */ @@ -184,28 +245,28 @@ DateMV (BasicCell *_cell, case '=': /* increment day */ date->tm_mday ++; - accel = 1; + accel = GNC_T; break; case '_': case '-': /* decrement day */ date->tm_mday --; - accel = 1; + accel = GNC_T; break; case '}': case ']': /* increment month */ date->tm_mon ++; - accel = 1; + accel = GNC_T; break; case '{': case '[': /* decrment month */ date->tm_mon --; - accel = 1; + accel = GNC_T; break; case 'M': @@ -261,7 +322,7 @@ DateMV (BasicCell *_cell, cell->cell.value = strdup (buff); datestr = strdup (buff); - + return datestr; } @@ -279,8 +340,8 @@ DateLeave (BasicCell *_cell, const char * curr) xaccParseDate (&(cell->date), curr); printDate (buff, cell->date.tm_mday, - cell->date.tm_mon+1, - cell->date.tm_year+1900); + cell->date.tm_mon+1, + cell->date.tm_year+1900); if (cell->cell.value) free (cell->cell.value); cell->cell.value = strdup (buff); @@ -346,6 +407,7 @@ xaccInitDateCell (DateCell *cell) cell->cell.modify_verify = DateMV; cell->cell.leave_cell = DateLeave; cell->cell.set_value = setDateCellValue; + cell->cell.get_help_value = DateCellHelpValue; } /* ================================================ */ @@ -456,8 +518,8 @@ setDateCellValue (BasicCell *_cell, const char *str) xaccParseDate (&(cell->date), str); printDate (buff, cell->date.tm_mday, - cell->date.tm_mon+1, - cell->date.tm_year+1900); + cell->date.tm_mon+1, + cell->date.tm_year+1900); if (cell->cell.value) free (cell->cell.value); cell->cell.value = strdup (buff); diff --git a/src/register/gnome/Makefile.in b/src/register/gnome/Makefile.in index 5814eee532..17864deccd 100644 --- a/src/register/gnome/Makefile.in +++ b/src/register/gnome/Makefile.in @@ -42,7 +42,7 @@ LIBS = -L$(prefix)/lib @LIBS@ \ # See Makefile.common for information about these variables. GNOME_SRCS := gnucash-sheet.c gnucash-grid.c gnucash-color.c gnucash-cursor.c \ gnucash-item-edit.c gnucash-style.c combocell-gnome.c \ - gnucash-header.c gnucash-item-list.c + gnucash-header.c gnucash-item-list.c quickfillcell-gnome.c ###################################################################### default: gnome diff --git a/src/register/gnome/combocell-gnome.c b/src/register/gnome/combocell-gnome.c index 328aaf7e26..55fdb55e8b 100644 --- a/src/register/gnome/combocell-gnome.c +++ b/src/register/gnome/combocell-gnome.c @@ -31,6 +31,8 @@ own strings. */ +#include "config.h" + #include #include "splitreg.h" @@ -41,6 +43,7 @@ #include "gnucash-item-edit.h" #include "gnucash-item-list.h" #include "global-options.h" +#include "messages.h" #include "util.h" @@ -53,8 +56,10 @@ typedef struct _PopBox GNCItemList *item_list; gint select_item_signal; + gint change_item_signal; gint key_press_signal; - gboolean list_signals_connected; + + gboolean signals_connected; /* list signals connected? */ gboolean list_in_sync; /* list in sync with menustrings? */ gboolean list_sorted; /* list has been sorted? */ @@ -64,13 +69,21 @@ typedef struct _PopBox gboolean in_list_select; gncBoolean strict; + + char complete_char; /* char to be used for auto-completion */ + + gchar *ignore_string; + gchar *ignore_help; } PopBox; +static void block_list_signals (ComboCell *cell); +static void unblock_list_signals (ComboCell *cell); static void realizeCombo (BasicCell *bcell, void *w, int width); static void moveCombo (BasicCell *bcell, int phys_row, int phys_col); static void destroyCombo (BasicCell *bcell); -static const char * enterCombo (BasicCell *bcell, const char *value, +static const char * enterCombo (BasicCell *bcell, + const char *value, int *cursor_position, int *start_selection, int *end_selection); @@ -101,13 +114,13 @@ void xaccInitComboCell (ComboCell *cell) cell->cell.realize = realizeCombo; cell->cell.destroy = destroyCombo; - box = g_new(PopBox, 1); + box = g_new0(PopBox, 1); box->sheet = NULL; box->item_edit = NULL; box->item_list = NULL; box->menustrings = NULL; - box->list_signals_connected = FALSE; + box->signals_connected = FALSE; box->list_in_sync = TRUE; box->list_sorted = TRUE; box->list_popped = FALSE; @@ -118,6 +131,11 @@ void xaccInitComboCell (ComboCell *cell) box->in_list_select = FALSE; box->strict = TRUE; + + box->complete_char = 0; + + box->ignore_string = NULL; + box->ignore_help = NULL; } /* =============================================== */ @@ -136,6 +154,17 @@ select_item_cb (GNCItemList *item_list, char *item_string, gpointer data) box->list_popped = FALSE; } +static void +change_item_cb (GNCItemList *item_list, char *item_string, gpointer data) +{ + ComboCell *cell = (ComboCell *) data; + PopBox *box = (PopBox *) cell->cell.gui_private; + + box->in_list_select = TRUE; + gnucash_sheet_modify_current_cell(box->sheet, item_string); + box->in_list_select = FALSE; +} + static void key_press_item_cb (GNCItemList *item_list, GdkEventKey *event, gpointer data) { @@ -155,11 +184,11 @@ key_press_item_cb (GNCItemList *item_list, GdkEventKey *event, gpointer data) } static void -disconnect_list_signals (ComboCell *cell) +combo_disconnect_signals (ComboCell *cell) { PopBox *box = (PopBox *) cell->cell.gui_private; - if (!box->list_signals_connected) + if (!box->signals_connected) return; if (GTK_OBJECT_DESTROYED(GTK_OBJECT(box->item_list))) @@ -168,18 +197,21 @@ disconnect_list_signals (ComboCell *cell) gtk_signal_disconnect(GTK_OBJECT(box->item_list), box->select_item_signal); + gtk_signal_disconnect(GTK_OBJECT(box->item_list), + box->change_item_signal); + gtk_signal_disconnect(GTK_OBJECT(box->item_list), box->key_press_signal); - box->list_signals_connected = FALSE; + box->signals_connected = FALSE; } static void -connect_list_signals (ComboCell *cell) +combo_connect_signals (ComboCell *cell) { PopBox *box = (PopBox *) cell->cell.gui_private; - if (box->list_signals_connected) + if (box->signals_connected) return; if (GTK_OBJECT_DESTROYED(GTK_OBJECT(box->item_list))) @@ -190,13 +222,54 @@ connect_list_signals (ComboCell *cell) GTK_SIGNAL_FUNC(select_item_cb), (gpointer) cell); + box->change_item_signal = + gtk_signal_connect(GTK_OBJECT(box->item_list), "change_item", + GTK_SIGNAL_FUNC(change_item_cb), + (gpointer) cell); + box->key_press_signal = gtk_signal_connect(GTK_OBJECT(box->item_list), "key_press_event", GTK_SIGNAL_FUNC(key_press_item_cb), (gpointer) cell); - box->list_signals_connected = TRUE; + box->signals_connected = TRUE; +} + +static void +block_list_signals (ComboCell *cell) +{ + PopBox *box = (PopBox *) cell->cell.gui_private; + + if (!box->signals_connected) + return; + + gtk_signal_handler_block(GTK_OBJECT(box->item_list), + box->select_item_signal); + + gtk_signal_handler_block(GTK_OBJECT(box->item_list), + box->change_item_signal); + + gtk_signal_handler_block(GTK_OBJECT(box->item_list), + box->key_press_signal); +} + +static void +unblock_list_signals (ComboCell *cell) +{ + PopBox *box = (PopBox *) cell->cell.gui_private; + + if (!box->signals_connected) + return; + + gtk_signal_handler_unblock(GTK_OBJECT(box->item_list), + box->select_item_signal); + + gtk_signal_handler_unblock(GTK_OBJECT(box->item_list), + box->change_item_signal); + + gtk_signal_handler_unblock(GTK_OBJECT(box->item_list), + box->key_press_signal); } /* =============================================== */ @@ -210,7 +283,7 @@ destroyCombo (BasicCell *bcell) if (cell->cell.realize == NULL) { if (box != NULL && box->item_list != NULL) { - disconnect_list_signals(cell); + combo_disconnect_signals(cell); gtk_object_unref(GTK_OBJECT(box->item_list)); box->item_list = NULL; } @@ -241,6 +314,9 @@ void xaccDestroyComboCell (ComboCell *cell) xaccFreeQuickFill(box->qf); box->qf = NULL; + g_free(box->ignore_string); + g_free(box->ignore_help); + g_free(box); cell->cell.gui_private = NULL; } @@ -276,8 +352,14 @@ xaccClearComboCellMenu (ComboCell * cell) box->qf = xaccMallocQuickFill(); if (box->item_list != NULL) + { + block_list_signals(cell); + gnc_item_list_clear(box->item_list); + unblock_list_signals(cell); + } + box->list_in_sync = TRUE; box->list_sorted = TRUE; } @@ -329,7 +411,15 @@ xaccAddComboCellMenuItem (ComboCell *cell, char * menustr) gnc_combo_sync_edit_list(box); if (box->item_list != NULL) + { + block_list_signals(cell); + gnc_item_list_append(box->item_list, menustr); + if (strcmp(menustr, cell->cell.value) == 0) + gnc_item_list_select(box->item_list, menustr); + + unblock_list_signals(cell); + } else box->list_in_sync = FALSE; @@ -357,7 +447,7 @@ ComboMV (BasicCell *_cell, int *start_selection, int *end_selection) { - QuickFillCell *cell = (QuickFillCell *) _cell; + ComboCell *cell = (ComboCell *) _cell; PopBox *box = cell->cell.gui_private; const char *retval; QuickFill *match; @@ -393,7 +483,11 @@ ComboMV (BasicCell *_cell, if ((match == NULL) || (match->text == NULL)) { xaccSetBasicCellValue (_cell, newval); + + block_list_signals(cell); gnc_item_list_select(box->item_list, NULL); + unblock_list_signals(cell); + return newval; } @@ -416,13 +510,9 @@ ComboMV (BasicCell *_cell, box->list_popped = TRUE; } - gtk_signal_handler_block(GTK_OBJECT(box->item_list), - box->select_item_signal); - + block_list_signals(cell); gnc_item_list_select(box->item_list, retval); - - gtk_signal_handler_unblock(GTK_OBJECT(box->item_list), - box->select_item_signal); + unblock_list_signals(cell); xaccSetBasicCellValue (_cell, retval); @@ -431,6 +521,176 @@ ComboMV (BasicCell *_cell, /* =============================================== */ +static gncBoolean +ComboDirect (BasicCell *bcell, + const char *oldval, + char **newval_ptr, + int *cursor_position, + int *start_selection, + int *end_selection, + void *gui_data) +{ + ComboCell *cell = (ComboCell *) bcell; + PopBox *box = cell->cell.gui_private; + GdkEventKey *event = gui_data; + gboolean keep_on_going = FALSE; + gboolean extra_colon; + QuickFill *match; + char *search; + int prefix_len; + int new_pos; + int length; + + if (event->type != GDK_KEY_PRESS) + return GNC_F; + + length = strlen(oldval); + + switch (event->keyval) { + case GDK_slash: + if (!(event->state & GDK_MOD1_MASK)) + { + if (event->keyval == box->complete_char) + break; + + return GNC_F; + } + keep_on_going = TRUE; + case GDK_Tab: + case GDK_ISO_Left_Tab: + if (!(event->state & GDK_CONTROL_MASK) && + !keep_on_going) + return GNC_F; + + match = xaccGetQuickFillStrLen(box->qf, oldval, + *cursor_position); + if (match == NULL) + return GNC_T; + + match = xaccGetQuickFillUniqueLen(match, &prefix_len); + if (match == NULL) + return GNC_T; + + if ((match->text != NULL) && + (strncmp(match->text, oldval, length) == 0) && + (strcmp(match->text, oldval) != 0)) + { + *newval_ptr = strdup(match->text); + assert(*newval_ptr != NULL); + + xaccSetBasicCellValue(bcell, *newval_ptr); + + block_list_signals(cell); + gnc_item_list_select(box->item_list, + *newval_ptr); + unblock_list_signals(cell); + + } + + *cursor_position += prefix_len; + *start_selection = *cursor_position; + *end_selection = -1; + + return GNC_T; + } + + if (box->complete_char == 0) + return GNC_F; + + if (event->keyval != box->complete_char) + return GNC_F; + + if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) + return GNC_F; + + if ((*cursor_position < length) && + ((*end_selection < length) || + (*cursor_position < *start_selection))) + return GNC_F; + + if ((*cursor_position == length) && + (*start_selection != *end_selection) && + (*end_selection < length)) + return GNC_F; + + search = NULL; + if (*cursor_position < length) + search = strchr(oldval + *cursor_position + 1, + box->complete_char); + + new_pos = *cursor_position; + + if (search != NULL) + { + new_pos = search - oldval; + extra_colon = FALSE; + } + else + { + new_pos = length; + extra_colon = TRUE; + } + + match = xaccGetQuickFillStrLen(box->qf, oldval, new_pos); + if (match == NULL) + return GNC_F; + + if (extra_colon) + { + match = xaccGetQuickFill(match, box->complete_char); + if (match == NULL) + return GNC_F; + + new_pos++; + } + + if ((match->text != NULL) && + (strncmp(match->text, oldval, length) == 0) && + (strcmp(match->text, oldval) != 0)) + { + *newval_ptr = strdup(match->text); + assert(*newval_ptr != NULL); + + xaccSetBasicCellValue(bcell, *newval_ptr); + + block_list_signals(cell); + gnc_item_list_select(box->item_list, *newval_ptr); + unblock_list_signals(cell); + } + + *cursor_position = new_pos; + *start_selection = new_pos; + *end_selection = -1; + + return GNC_T; +} + +/* =============================================== */ + +static char * +ComboHelpValue(BasicCell *bcell) +{ + ComboCell *cell = (ComboCell *) bcell; + PopBox *box = cell->cell.gui_private; + + if ((bcell->value != NULL) && (bcell->value[0] != 0)) + { + if ((box->ignore_string != NULL) && + (box->ignore_help != NULL) && + (safe_strcmp(bcell->value, box->ignore_string) == 0)) + return strdup(box->ignore_help); + + return strdup(bcell->value); + } + + if (bcell->blank_help != NULL) + return strdup(bcell->blank_help); + + return NULL; +} + +/* =============================================== */ + static void realizeCombo (BasicCell *bcell, void *data, int pixel_width) { @@ -454,6 +714,8 @@ realizeCombo (BasicCell *bcell, void *data, int pixel_width) cell->cell.leave_cell = leaveCombo; cell->cell.destroy = destroyCombo; cell->cell.modify_verify = ComboMV; + cell->cell.direct_update = ComboDirect; + cell->cell.get_help_value = ComboHelpValue; } /* =============================================== */ @@ -463,7 +725,7 @@ moveCombo (BasicCell *bcell, int phys_row, int phys_col) { PopBox *box = (PopBox *) bcell->gui_private; - disconnect_list_signals((ComboCell *) bcell); + combo_disconnect_signals((ComboCell *) bcell); gnome_canvas_item_set(GNOME_CANVAS_ITEM(box->item_edit), "is_combo", FALSE, NULL); @@ -476,13 +738,19 @@ moveCombo (BasicCell *bcell, int phys_row, int phys_col) /* =============================================== */ static const char * -enterCombo (BasicCell *bcell, const char *value, +enterCombo (BasicCell *bcell, + const char *value, int *cursor_position, int *start_selection, int *end_selection) { + ComboCell *cell = (ComboCell *) bcell; PopBox *box = (PopBox *) bcell->gui_private; + if ((box->ignore_string != NULL) && + (safe_strcmp(value, box->ignore_string) == 0)) + return strdup(value); + gnc_combo_sync_edit_list(box); gnc_combo_sort_edit_list(box); @@ -491,9 +759,11 @@ enterCombo (BasicCell *bcell, const char *value, gnome_canvas_item_set(GNOME_CANVAS_ITEM(box->item_edit), "is_combo", TRUE, NULL); + block_list_signals(cell); gnc_item_list_select(box->item_list, bcell->value); + unblock_list_signals(cell); - connect_list_signals((ComboCell *) bcell); + combo_connect_signals((ComboCell *) bcell); *cursor_position = -1; *start_selection = 0; @@ -511,7 +781,7 @@ leaveCombo (BasicCell *bcell, const char *value) PopBox *box = (PopBox *) bcell->gui_private; - disconnect_list_signals((ComboCell *) bcell); + combo_disconnect_signals((ComboCell *) bcell); gnome_canvas_item_set(GNOME_CANVAS_ITEM(box->item_edit), "is_combo", FALSE, NULL); @@ -526,8 +796,10 @@ leaveCombo (BasicCell *bcell, const char *value) (gpointer) value, (GCompareFunc) safe_strcmp); - /* hack, "Split" is ok, even though it's not in list */ - if (find == NULL && (safe_strcmp(value, "Split") != 0)) + /* The ignore string is ok, even if it's not in list. */ + if (find == NULL && + ((box->ignore_string == NULL) || + (safe_strcmp(value, box->ignore_string) != 0))) return strdup(""); } @@ -549,6 +821,51 @@ xaccComboCellSetStrict (ComboCell *cell, gncBoolean strict) box->strict = strict; } +/* =============================================== */ + +void +xaccComboCellSetCompleteChar (ComboCell *cell, char complete_char) +{ + PopBox *box; + + if (cell == NULL) + return; + + box = (PopBox *) cell->cell.gui_private; + + box->complete_char = complete_char; +} + +/* =============================================== */ + +void +xaccComboCellSetIgnoreString (ComboCell *cell, const char *ignore_string) +{ + PopBox *box; + + if (cell == NULL) + return; + + box = (PopBox *) cell->cell.gui_private; + + box->ignore_string = g_strdup(ignore_string); +} + +/* =============================================== */ + +void +xaccComboCellSetIgnoreHelp (ComboCell *cell, const char *ignore_help) +{ + PopBox *box; + + if (cell == NULL) + return; + + box = (PopBox *) cell->cell.gui_private; + + box->ignore_help = g_strdup(ignore_help); +} + /* =============== end of file =================== */ diff --git a/src/register/gnome/gnucash-grid.c b/src/register/gnome/gnucash-grid.c index 2d46d4f0dd..6bca583cee 100644 --- a/src/register/gnome/gnucash-grid.c +++ b/src/register/gnome/gnucash-grid.c @@ -254,11 +254,10 @@ draw_cell (GnucashGrid *grid, int block, sheet_block = gnucash_sheet_get_block (grid->sheet, block, 0); gdk_gc_set_foreground (grid->gc, sheet_block->bg_colors[i][j]); - gdk_draw_rectangle (drawable, grid->gc, TRUE, x, y, width, height); gdk_gc_set_foreground (grid->gc, &gn_black); - + /* draw the border */ gdk_draw_rectangle (drawable, grid->gc, FALSE, x, y, width, height); @@ -268,10 +267,9 @@ draw_cell (GnucashGrid *grid, int block, font = style->fonts[i][j]; else font = grid->normal_font; - - gdk_gc_set_foreground (grid->gc, &gn_black); + gdk_gc_set_foreground (grid->gc, sheet_block->fg_colors[i][j]); - + if (table->current_cursor_virt_row == block && (!text || strlen(text) == 0)) { font = grid->italic_font; diff --git a/src/register/gnome/gnucash-header.c b/src/register/gnome/gnucash-header.c index 59a69ce256..4ed868a0a9 100644 --- a/src/register/gnome/gnucash-header.c +++ b/src/register/gnome/gnucash-header.c @@ -60,28 +60,35 @@ gnucash_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable, { GnucashHeader *header = GNUCASH_HEADER(item); SheetBlockStyle *style = header->style; + SheetBlockStyle *header_style; int i, j; int xpaint, ypaint; int w = 0, h = 0; gchar *text; GdkFont *font; - gdk_gc_set_foreground(header->gc, &gn_white); + header_style = header->sheet->cursor_style[GNUCASH_CURSOR_HEADER]; + + /* Assume all cells have the same color */ + gdk_gc_set_foreground(header->gc, + header_style->inactive_bg_color[0][0]); gdk_draw_rectangle(drawable, header->gc, TRUE, 0, 0, width, height); gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, -1, -1); gdk_gc_set_foreground (header->gc, &gn_black); - gdk_draw_rectangle (drawable, header->gc, FALSE, - -x, -y, style->dimensions->width-1, style->dimensions->height); - gdk_draw_line (drawable, header->gc, - -x, style->dimensions->height-1, style->dimensions->width-1, style->dimensions->height-1); + gdk_draw_rectangle (drawable, header->gc, FALSE, -x, -y, + style->dimensions->width, + style->dimensions->height); + gdk_draw_line (drawable, header->gc, -x, + style->dimensions->height - 1, + style->dimensions->width - 1, + style->dimensions->height - 1); gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, -1, -1); gdk_gc_set_background (header->gc, &gn_white); gdk_gc_set_foreground (header->gc, &gn_black); font = style->header_font; - ypaint = -y; for (i = 0; i < style->nrows; i++) { @@ -185,8 +192,13 @@ gnucash_header_unrealize (GnomeCanvasItem *item) header->gc = NULL; } - gdk_cursor_destroy (header->resize_cursor); - gdk_cursor_destroy (header->normal_cursor); + if (header->resize_cursor != NULL) + gdk_cursor_destroy (header->resize_cursor); + header->resize_cursor = NULL; + + if (header->normal_cursor != NULL) + gdk_cursor_destroy (header->normal_cursor); + header->normal_cursor = NULL; if (GNOME_CANVAS_ITEM_CLASS (gnucash_header_parent_class)->unrealize) (*GNOME_CANVAS_ITEM_CLASS @@ -200,15 +212,14 @@ gnucash_header_destroy (GtkObject *object) if (GTK_OBJECT_CLASS (gnucash_header_parent_class)->destroy) (*GTK_OBJECT_CLASS (gnucash_header_parent_class)->destroy)(object); - } void gnucash_header_reconfigure (GnucashHeader *header) { GnomeCanvas *canvas = GNOME_CANVAS_ITEM(header)->canvas; - int w, h; double old_w, old_h; + int w, h; header->style = header->sheet->cursor_style[header->type]; @@ -245,23 +256,30 @@ gnucash_header_point (GnomeCanvasItem *item, } /* - * Returns -1 if pointer not on a resize line, else returns - * the index of the column to the left. + * Returns FALSE if pointer not on a resize line, else returns + * TRUE. Returns the index of the column to the left in the col + * argument. */ -static int -pointer_on_resize_line (GnucashHeader *header, int x, int y) +static gboolean +pointer_on_resize_line (GnucashHeader *header, int x, int y, int *col) { SheetBlockStyle *style = header->style; + gboolean on_the_line = FALSE; + int pixels = 0; /* = style->dimensions->pixel_widths[header->row][0];*/ int j; - int pixels = style->dimensions->pixel_widths[header->row][0]; - for (j = 1; j < style->ncols; j++) { - if (x >= pixels - 1 && x <= pixels+1) - return j-1; + for (j = 0; j < style->ncols; j++) { pixels += style->dimensions->pixel_widths[header->row][j]; + if (x >= pixels - 1 && x <= pixels + 1) + on_the_line = TRUE; + if (x <= pixels + 1) + break; } - return -1; + if (col != NULL) + *col = j; + + return on_the_line; } @@ -275,7 +293,8 @@ find_resize_col (GnucashHeader *header, int col) return -1; /* skip to the right over zero-width columns */ - while (col+1 < style->ncols && style->dimensions->pixel_widths[0][col+1] == 0) + while (col+1 < style->ncols && + style->dimensions->pixel_widths[0][col+1] == 0) col++; /* now go back left till we have a resizable column */ @@ -286,11 +305,30 @@ find_resize_col (GnucashHeader *header, int col) col--; } - /* didn't find a resizable column to the right of col */ + /* didn't find a resizable column to the right of col */ return -1; } +static void +gnucash_header_auto_resize_column (GnucashHeader *header, gint col) +{ + GnucashSheet *sheet = header->sheet; + int width = gnucash_sheet_col_max_width (sheet, 0, col); + + gnucash_sheet_style_set_col_width (sheet, header->style, + col, width, FALSE); + + gnucash_sheet_style_set_dimensions (sheet, header->style); + + gnucash_cursor_configure (GNUCASH_CURSOR(sheet->cursor)); + item_edit_configure (ITEM_EDIT(sheet->item_editor)); + + gnucash_sheet_update_adjustments (sheet); + + gnucash_header_request_redraw (header); + gnucash_sheet_redraw_all (sheet); +} static gint gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) @@ -300,7 +338,6 @@ gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) int x, y; int col; - switch (event->type) { case GDK_MOTION_NOTIFY: @@ -321,7 +358,7 @@ gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) } new_width = header->resize_col_width + change; - + if (new_width >= 0) { header->resize_x = x; header->resize_col_width = new_width; @@ -330,10 +367,9 @@ gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) break; } - - if ((col = pointer_on_resize_line(header, x, y)) > -1 && - gnucash_style_col_is_resizable (header->style, col) ) + if (pointer_on_resize_line(header, x, y, &col) && + gnucash_style_col_is_resizable (header->style, col)) gdk_window_set_cursor (GTK_WIDGET(canvas)->window, header->resize_cursor); else @@ -344,13 +380,18 @@ gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) case GDK_BUTTON_PRESS: { int col; - + + if (event->button.button != 1) + break; + gnome_canvas_w2c (canvas, event->button.x, event->button.y, &x, &y); - col = pointer_on_resize_line (header, x, y); - col = find_resize_col (header, col); - + if (pointer_on_resize_line (header, x, y, &col)) + col = find_resize_col (header, col); + else + col = -1; + if (col > -1) { header->in_resize = TRUE; header->resize_col = col; @@ -363,7 +404,10 @@ gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) case GDK_BUTTON_RELEASE: { GnucashSheet *sheet = header->sheet; - + + if (event->button.button != 1) + break; + gnome_canvas_w2c (canvas, event->button.x, event->button.y, &x, &y); @@ -371,31 +415,59 @@ gnucash_header_event (GnomeCanvasItem *item, GdkEvent *event) if (header->needs_ungrab) { gnome_canvas_item_ungrab (item, event->button.time); header->needs_ungrab = FALSE; + + gnucash_sheet_style_set_col_width (sheet, header->style, + header->resize_col, header->resize_col_width, FALSE); + gnucash_sheet_style_set_dimensions (sheet, header->style); + + gnucash_cursor_configure (GNUCASH_CURSOR(sheet->cursor)); + item_edit_configure (ITEM_EDIT(sheet->item_editor)); + + gnucash_sheet_update_adjustments (sheet); + + gnucash_header_request_redraw (header); + gnucash_sheet_redraw_all (sheet); } - - gnucash_sheet_style_set_col_width (sheet, header->style, - header->resize_col, header->resize_col_width, TRUE); - gnucash_sheet_style_set_dimensions (sheet, header->style); - header->in_resize = FALSE; header->resize_col = -1; - - gnucash_cursor_configure (GNUCASH_CURSOR(sheet->cursor)); - item_edit_configure (ITEM_EDIT(sheet->item_editor)); - - gnucash_sheet_update_adjustments (sheet); - - gnucash_header_request_redraw (header); - gnucash_sheet_redraw_all (sheet); } - + break; } - + + case GDK_2BUTTON_PRESS: + { + gboolean on_line; + int ptr_col; + int resize_col; + + if (event->button.button != 1) + break; + + gnome_canvas_w2c (canvas, event->button.x, event->button.y, + &x, &y); + + on_line = pointer_on_resize_line (header, x, y, &ptr_col); + resize_col = find_resize_col (header, ptr_col); + + if ((resize_col > -1) && + (on_line || (resize_col == ptr_col))) { + header->in_resize = FALSE; + header->resize_col = -1; + if (header->needs_ungrab) { + gnome_canvas_item_ungrab (item, event->button.time); + header->needs_ungrab = FALSE; + } + + gnucash_header_auto_resize_column (header, resize_col); + } + + } + break; + default: break; } - return TRUE; } @@ -441,7 +513,7 @@ gnucash_header_init (GnucashHeader *header) header->in_resize = FALSE; header->resize_col = -1; header->resize_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); - header->normal_cursor = gdk_cursor_new (GDK_X_CURSOR); + header->normal_cursor = NULL; } diff --git a/src/register/gnome/gnucash-header.h b/src/register/gnome/gnucash-header.h index 77343bbc3c..32764a5045 100644 --- a/src/register/gnome/gnucash-header.h +++ b/src/register/gnome/gnucash-header.h @@ -40,7 +40,7 @@ typedef struct { int resize_x; int resize_col; int needs_ungrab; - + GdkGC *gc; GdkCursor *normal_cursor; GdkCursor *resize_cursor; diff --git a/src/register/gnome/gnucash-item-edit.c b/src/register/gnome/gnucash-item-edit.c index 73e179188f..57c605b99f 100644 --- a/src/register/gnome/gnucash-item-edit.c +++ b/src/register/gnome/gnucash-item-edit.c @@ -28,7 +28,25 @@ #include "util.h" +/* The arguments we take */ +enum { + ARG_0, + ARG_SHEET, /* The Sheet argument */ + ARG_GTK_ENTRY, /* The GtkEntry argument */ + ARG_IS_COMBO, /* Should this be a combo? */ +}; + +/* values for selection info */ +enum { + TARGET_STRING, + TARGET_TEXT, + TARGET_COMPOUND_TEXT +}; + static GnomeCanvasItemClass *item_edit_parent_class; +static GdkAtom clipboard_atom = GDK_NONE; +static GdkAtom ctext_atom = GDK_NONE; + typedef struct _TextDrawInfo TextDrawInfo; struct _TextDrawInfo @@ -63,15 +81,6 @@ struct _TextDrawInfo }; -/* The arguments we take */ -enum { - ARG_0, - ARG_SHEET, /* The Sheet argument */ - ARG_GTK_ENTRY, /* The GtkEntry argument */ - ARG_IS_COMBO, /* Should this be a combo? */ -}; - - static void item_edit_show_combo_toggle (ItemEdit *item_edit, gint x, gint y, gint width, gint height, @@ -364,6 +373,10 @@ item_edit_init (ItemEdit *item_edit) item_edit->editor = NULL; item_edit->clipboard = NULL; + item_edit->has_selection = FALSE; + item_edit->is_combo = FALSE; + item_edit->show_list = FALSE; + item_edit->combo_toggle.combo_button = NULL; item_edit->combo_toggle.combo_button_item = NULL; item_edit->combo_toggle.toggle_offset = 0; @@ -393,6 +406,14 @@ queue_sync (ItemEdit *item_edit) gnome_canvas_request_redraw (canvas, x, y, x+w, y+h); } +void +item_edit_redraw (ItemEdit *item_edit) +{ + g_return_if_fail(item_edit != NULL); + g_return_if_fail(IS_ITEM_EDIT(item_edit)); + + queue_sync(item_edit); +} static void entry_changed (GtkEntry *entry, void *data) @@ -584,8 +605,42 @@ item_edit_configure (ItemEdit *item_edit) } +void +item_edit_claim_selection (ItemEdit *item_edit, guint32 time) +{ + GtkEditable *editable; + gint start_sel, end_sel; + + g_return_if_fail(item_edit != NULL); + g_return_if_fail(IS_ITEM_EDIT(item_edit)); + + editable = GTK_EDITABLE (item_edit->editor); + + start_sel = MIN(editable->selection_start_pos, + editable->selection_end_pos); + end_sel = MAX(editable->selection_start_pos, + editable->selection_end_pos); + + if (start_sel != end_sel) + { + gtk_selection_owner_set (GTK_WIDGET(item_edit->sheet), + GDK_SELECTION_PRIMARY, time); + item_edit->has_selection = TRUE; + } + else + { + GdkWindow *owner; + + owner = gdk_selection_owner_get (GDK_SELECTION_PRIMARY); + if (owner == GTK_WIDGET(item_edit->sheet)->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time); + item_edit->has_selection = FALSE; + } +} + + static void -item_edit_cut_copy_clipboard (ItemEdit *item_edit, gboolean cut) +item_edit_cut_copy_clipboard (ItemEdit *item_edit, guint32 time, gboolean cut) { GtkEditable *editable; gint start_sel, end_sel; @@ -604,10 +659,14 @@ item_edit_cut_copy_clipboard (ItemEdit *item_edit, gboolean cut) if (start_sel == end_sel) return; - if (item_edit->clipboard != NULL) - g_free(item_edit->clipboard); + g_free(item_edit->clipboard); + + if (gtk_selection_owner_set (GTK_WIDGET(item_edit->sheet), + clipboard_atom, time)) + clip = gtk_editable_get_chars (editable, start_sel, end_sel); + else + clip = NULL; - clip = gtk_editable_get_chars(editable, start_sel, end_sel); item_edit->clipboard = clip; if (!cut) @@ -620,53 +679,44 @@ item_edit_cut_copy_clipboard (ItemEdit *item_edit, gboolean cut) void -item_edit_cut_clipboard (ItemEdit *item_edit) +item_edit_cut_clipboard (ItemEdit *item_edit, guint32 time) { - item_edit_cut_copy_clipboard(item_edit, TRUE); + item_edit_cut_copy_clipboard(item_edit, time, TRUE); } void -item_edit_copy_clipboard (ItemEdit *item_edit) +item_edit_copy_clipboard (ItemEdit *item_edit, guint32 time) { - item_edit_cut_copy_clipboard(item_edit, FALSE); + item_edit_cut_copy_clipboard(item_edit, time, FALSE); } void -item_edit_paste_clipboard (ItemEdit *item_edit) +item_edit_paste_clipboard (ItemEdit *item_edit, guint32 time) { - GtkEditable *editable; - gint start_sel, end_sel; - gint current_pos; - gchar *clip; - g_return_if_fail(item_edit != NULL); g_return_if_fail(IS_ITEM_EDIT(item_edit)); - if ((item_edit->clipboard == NULL) || - (item_edit->clipboard[0] == 0)) - return; + if (ctext_atom == GDK_NONE) + ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); - editable = GTK_EDITABLE (item_edit->editor); + gtk_selection_convert(GTK_WIDGET(item_edit->sheet), + clipboard_atom, ctext_atom, time); +} - current_pos = editable->current_pos; - start_sel = MIN(editable->selection_start_pos, - editable->selection_end_pos); - end_sel = MAX(editable->selection_start_pos, - editable->selection_end_pos); - if (start_sel != end_sel) - { - gtk_editable_delete_text(editable, start_sel, end_sel); - current_pos = start_sel; - } +void +item_edit_paste_primary (ItemEdit *item_edit, guint32 time) +{ + g_return_if_fail(item_edit != NULL); + g_return_if_fail(IS_ITEM_EDIT(item_edit)); - clip = item_edit->clipboard; - gtk_editable_insert_text(editable, clip, strlen(clip), ¤t_pos); + if (ctext_atom == GDK_NONE) + ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); - gtk_editable_select_region(editable, 0, 0); - gtk_editable_set_position(editable, current_pos); + gtk_selection_convert(GTK_WIDGET(item_edit->sheet), + GDK_SELECTION_PRIMARY, ctext_atom, time); } @@ -871,6 +921,7 @@ item_edit_class_init (ItemEditClass *item_edit_class) item_class->realize = item_edit_realize; } + GtkType item_edit_get_type (void) { @@ -921,6 +972,13 @@ create_combo_toggle(GnomeCanvasGroup *parent, ComboToggle *ct) GnomeCanvasItem * item_edit_new (GnomeCanvasGroup *parent, GnucashSheet *sheet, GtkWidget *entry) { + static const GtkTargetEntry targets[] = { + { "STRING", 0, TARGET_STRING }, + { "TEXT", 0, TARGET_TEXT }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } + }; + static const gint n_targets = sizeof(targets) / sizeof(targets[0]); + GnomeCanvasItem *item; ItemEdit *item_edit; @@ -936,6 +994,17 @@ item_edit_new (GnomeCanvasGroup *parent, GnucashSheet *sheet, GtkWidget *entry) create_combo_toggle(parent, &item_edit->combo_toggle); + if (clipboard_atom == GDK_NONE) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); + + gtk_selection_add_targets (GTK_WIDGET(sheet), + GDK_SELECTION_PRIMARY, + targets, n_targets); + + gtk_selection_add_targets (GTK_WIDGET(sheet), + clipboard_atom, + targets, n_targets); + return item; } @@ -1046,6 +1115,216 @@ item_edit_set_list (ItemEdit *item_edit, GNCItemList *item_list) item_edit_update (GNOME_CANVAS_ITEM(item_edit), NULL, NULL, 0); } + +void +item_edit_set_has_selection (ItemEdit *item_edit, gboolean has_selection) +{ + g_return_if_fail(item_edit != NULL); + g_return_if_fail(IS_ITEM_EDIT(item_edit)); + + item_edit->has_selection = has_selection; +} + +gboolean +item_edit_selection_clear (ItemEdit *item_edit, + GdkEventSelection *event) +{ + g_return_val_if_fail(item_edit != NULL, FALSE); + g_return_val_if_fail(IS_ITEM_EDIT(item_edit), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + + /* Let the selection handling code know that the selection + * has been changed, since we've overriden the default handler */ + if (!gtk_selection_clear(GTK_WIDGET(item_edit->sheet), event)) + return FALSE; + + if (event->selection == GDK_SELECTION_PRIMARY) + { + if (item_edit->has_selection) + { + item_edit->has_selection = FALSE; + /* TODO: redraw differently? */ + } + } + else if (event->selection == clipboard_atom) + { + g_free(item_edit->clipboard); + item_edit->clipboard = NULL; + } + + return TRUE; +} + + +void +item_edit_selection_get (ItemEdit *item_edit, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkEditable *editable; + + gint start_pos; + gint end_pos; + + gchar *str; + gint length; + + g_return_if_fail(item_edit != NULL); + g_return_if_fail(IS_ITEM_EDIT(item_edit)); + + editable = GTK_EDITABLE (item_edit->editor); + + if (selection_data->selection == GDK_SELECTION_PRIMARY) + { + start_pos = MIN(editable->selection_start_pos, + editable->selection_end_pos); + end_pos = MAX(editable->selection_start_pos, + editable->selection_end_pos); + + str = gtk_editable_get_chars(editable, start_pos, end_pos); + } + else /* CLIPBOARD */ + str = item_edit->clipboard; + + if (str == NULL) + return; + + length = strlen(str); + + if (info == TARGET_STRING) + { + gtk_selection_data_set (selection_data, + GDK_SELECTION_TYPE_STRING, + 8 * sizeof(gchar), (guchar *) str, + length); + } + else if ((info == TARGET_TEXT) || (info == TARGET_COMPOUND_TEXT)) + { + guchar *text; + gchar c; + GdkAtom encoding; + gint format; + gint new_length; + + c = str[length]; + str[length] = '\0'; + + gdk_string_to_compound_text(str, &encoding, &format, + &text, &new_length); + + gtk_selection_data_set(selection_data, encoding, + format, text, new_length); + + gdk_free_compound_text(text); + + str[length] = c; + } + + if (str != item_edit->clipboard) + g_free(str); +} + + +void +item_edit_selection_received (ItemEdit *item_edit, + GtkSelectionData *selection_data, + guint time) +{ + GtkEditable *editable; + gboolean reselect; + gint old_pos; + gint tmp_pos; + enum {INVALID, STRING, CTEXT} type; + + g_return_if_fail(item_edit != NULL); + g_return_if_fail(IS_ITEM_EDIT(item_edit)); + + editable = GTK_EDITABLE(item_edit->editor); + + if (selection_data->type == GDK_TARGET_STRING) + type = STRING; + else if ((selection_data->type == + gdk_atom_intern("COMPOUND_TEXT", FALSE)) || + (selection_data->type == gdk_atom_intern("TEXT", FALSE))) + type = CTEXT; + else + type = INVALID; + + if (type == INVALID || selection_data->length < 0) + { + /* avoid infinite loop */ + if (selection_data->target != GDK_TARGET_STRING) + gtk_selection_convert(GTK_WIDGET(item_edit->sheet), + selection_data->selection, + GDK_TARGET_STRING, time); + return; + } + + reselect = FALSE; + + if ((editable->selection_start_pos != editable->selection_end_pos) && + (!item_edit->has_selection || + (selection_data->selection == clipboard_atom))) + { + reselect = TRUE; + + gtk_editable_delete_text(editable, + MIN(editable->selection_start_pos, + editable->selection_end_pos), + MAX(editable->selection_start_pos, + editable->selection_end_pos)); + } + + tmp_pos = old_pos = editable->current_pos; + + switch (type) + { + case STRING: + selection_data->data[selection_data->length] = 0; + + gtk_editable_insert_text + (editable, (gchar *) selection_data->data, + strlen((gchar *)selection_data->data), + &tmp_pos); + + gtk_editable_set_position(editable, tmp_pos); + break; + case CTEXT: { + gchar **list; + gint count; + gint i; + + count = gdk_text_property_to_text_list + (selection_data->type, selection_data->format, + selection_data->data, selection_data->length, + &list); + + for (i = 0; i < count; i++) + { + gtk_editable_insert_text(editable, + list[i], + strlen(list[i]), + &tmp_pos); + + gtk_editable_set_position(editable, tmp_pos); + } + + if (count > 0) + gdk_free_text_list(list); + } + break; + case INVALID: /* quiet compiler */ + break; + } + + if (!reselect) + return; + + gtk_editable_select_region(editable, old_pos, editable->current_pos); +} + + /* Local Variables: c-basic-offset: 8 diff --git a/src/register/gnome/gnucash-item-edit.h b/src/register/gnome/gnucash-item-edit.h index 997a01d515..e00d876dc0 100644 --- a/src/register/gnome/gnucash-item-edit.h +++ b/src/register/gnome/gnucash-item-edit.h @@ -63,6 +63,7 @@ typedef struct guint signal; /* the signal we connect */ guint signal2; /* the other signal we connect */ + gboolean has_selection; gboolean is_combo; gboolean show_list; @@ -101,9 +102,28 @@ gboolean item_edit_set_cursor_pos (ItemEdit *item_edit, gboolean changed_cells, gboolean extend_selection); -void item_edit_cut_clipboard (ItemEdit *item_edit); -void item_edit_copy_clipboard (ItemEdit *item_edit); -void item_edit_paste_clipboard (ItemEdit *item_edit); +void item_edit_redraw (ItemEdit *item_edit); + +void item_edit_claim_selection (ItemEdit *item_edit, guint32 time); + +void item_edit_cut_clipboard (ItemEdit *item_edit, guint32 time); +void item_edit_copy_clipboard (ItemEdit *item_edit, guint32 time); +void item_edit_paste_clipboard (ItemEdit *item_edit, guint32 time); +void item_edit_paste_primary (ItemEdit *item_edit, guint32 time); + +void item_edit_set_has_selection (ItemEdit *item_edit, gboolean has_selection); + +gboolean item_edit_selection_clear (ItemEdit *item_edit, + GdkEventSelection *event); + +void item_edit_selection_get (ItemEdit *item_edit, + GtkSelectionData *selection_data, + guint info, + guint time); + +void item_edit_selection_received (ItemEdit *item_edit, + GtkSelectionData *selection_data, + guint time); typedef struct { GnomeCanvasItemClass parent_class; diff --git a/src/register/gnome/gnucash-item-list.c b/src/register/gnome/gnucash-item-list.c index 3e241fb1b0..fe7870f3bf 100644 --- a/src/register/gnome/gnucash-item-list.c +++ b/src/register/gnome/gnucash-item-list.c @@ -28,6 +28,7 @@ enum { SELECT_ITEM, + CHANGE_ITEM, KEY_PRESS_EVENT, LAST_SIGNAL }; @@ -144,19 +145,64 @@ gnc_item_list_button_event(GtkWidget *widget, GdkEventButton *event, } +static gboolean +gnc_clist_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + GtkAdjustment *vadj; + gfloat multiplier = 1.0; + gfloat v_value; + + vadj = gtk_clist_get_vadjustment(GTK_CLIST(widget)); + v_value = vadj->value; + if (event->state & GDK_SHIFT_MASK) + multiplier = 5.0; + + switch (event->button) + { + case 4: + v_value -= vadj->step_increment * multiplier; + break; + case 5: + v_value += vadj->step_increment * multiplier; + break; + default: + return FALSE; + } + + v_value = CLAMP(v_value, vadj->lower, vadj->upper - vadj->page_size); + + gtk_adjustment_set_value(vadj, v_value); + + return FALSE; +} + + static gboolean gnc_item_list_key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { GNCItemList *item_list = GNC_ITEM_LIST(data); + GtkCList *clist; + gboolean got_text; + gchar *string; + gint row; switch (event->keyval) { - case GDK_space: - if (event->state & GDK_CONTROL_MASK) - { - event->state &= ~GDK_CONTROL_MASK; + case GDK_Return: + clist = item_list->clist; + row = clist->focus_row; + if (row < 0) return FALSE; - } - break; + + got_text = gtk_clist_get_text(clist, row, 0, &string); + + if (!got_text) + return FALSE; + + gtk_signal_emit(GTK_OBJECT(item_list), + gnc_item_list_signals[SELECT_ITEM], + string); + + return TRUE; case GDK_Page_Up: case GDK_Page_Down: case GDK_Up: @@ -197,6 +243,16 @@ gnc_item_list_class_init(GNCItemListClass *item_list_class) GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); + gnc_item_list_signals[CHANGE_ITEM] = + gtk_signal_new("change_item", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET(GNCItemListClass, + change_item), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + gnc_item_list_signals[KEY_PRESS_EVENT] = gtk_signal_new ("key_press_event", GTK_RUN_LAST, @@ -210,6 +266,10 @@ gnc_item_list_class_init(GNCItemListClass *item_list_class) gtk_object_class_add_signals(object_class, gnc_item_list_signals, LAST_SIGNAL); + + item_list_class->select_item = NULL; + item_list_class->change_item = NULL; + item_list_class->key_press_event = NULL; } @@ -249,13 +309,17 @@ clist_select_row_cb(GtkCList *clist, gint row, gint column, gboolean got_text; char *string; - got_text = gtk_clist_get_text(clist, row, column, &string); + got_text = gtk_clist_get_text(clist, row, 0, &string); if (!got_text) return; - gtk_signal_emit(GTK_OBJECT(item_list), - gnc_item_list_signals[SELECT_ITEM], string); + if (column < 0) + gtk_signal_emit(GTK_OBJECT(item_list), + gnc_item_list_signals[CHANGE_ITEM], string); + else + gtk_signal_emit(GTK_OBJECT(item_list), + gnc_item_list_signals[SELECT_ITEM], string); } @@ -271,6 +335,7 @@ gnc_item_list_new(GnomeCanvasGroup *parent) clist = gtk_clist_new(1); gtk_box_pack_start(GTK_BOX(hbox), clist, TRUE, TRUE, 0); gtk_clist_set_column_auto_resize(GTK_CLIST(clist), 0, TRUE); + gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE); scrollbar = gtk_vscrollbar_new(NULL); gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0); @@ -290,7 +355,11 @@ gnc_item_list_new(GnomeCanvasGroup *parent) gtk_signal_connect_after(GTK_OBJECT(hbox), "button_press_event", GTK_SIGNAL_FUNC(gnc_item_list_button_event), - NULL); + (gpointer) item_list); + + gtk_signal_connect(GTK_OBJECT(clist), "button_press_event", + GTK_SIGNAL_FUNC(gnc_clist_button_event), + (gpointer) item_list); gtk_signal_connect(GTK_OBJECT(clist), "key_press_event", GTK_SIGNAL_FUNC(gnc_item_list_key_event), diff --git a/src/register/gnome/gnucash-item-list.h b/src/register/gnome/gnucash-item-list.h index 26997aff37..a3078ba9c6 100644 --- a/src/register/gnome/gnucash-item-list.h +++ b/src/register/gnome/gnucash-item-list.h @@ -57,6 +57,9 @@ typedef struct void (*select_item) (GNCItemList *item_list, char *item_string); + void (*change_item) (GNCItemList *item_list, + char *item_string); + void (*key_press_event) (GNCItemList *item_list, GdkEventKey *event); diff --git a/src/register/gnome/gnucash-sheet.c b/src/register/gnome/gnucash-sheet.c index b14bf24952..f416549e57 100644 --- a/src/register/gnome/gnucash-sheet.c +++ b/src/register/gnome/gnucash-sheet.c @@ -36,8 +36,8 @@ #define DEFAULT_REGISTER_HEIGHT 400 #define DEFAULT_REGISTER_WIDTH 630 -#define DEFAULT_REGISTER_ROWS 15 +static guint gnucash_register_initial_rows = 15; static void gnucash_sheet_cell_set_from_table (GnucashSheet *sheet, gint virt_row, gint virt_col, @@ -72,6 +72,12 @@ static GtkTableClass *register_parent_class; static guint register_signals[LAST_SIGNAL]; +void +gnucash_register_set_initial_rows(guint num_rows) +{ + gnucash_register_initial_rows = num_rows; +} + gint gnucash_sheet_cell_valid (GnucashSheet *sheet, gint virt_row, gint virt_col, gint cell_row, gint cell_col) @@ -161,7 +167,6 @@ gnucash_sheet_cursor_set_from_table (GnucashSheet *sheet, gncBoolean do_scroll) table->current_cursor_virt_col, cell_row, cell_col); - } @@ -393,7 +398,6 @@ gnucash_sheet_block_pixel_origin (GnucashSheet *sheet, gint vrow, gint vcol, g_return_if_fail (sheet != NULL); g_return_if_fail (GNUCASH_IS_SHEET(sheet)); - if ( vrow <= 0 || vrow > sheet->num_virt_rows || vcol < 0 || vcol > sheet->num_virt_cols) return; @@ -796,18 +800,18 @@ compute_optimal_width (GnucashSheet *sheet) { SheetBlockStyle *style; - if (sheet->default_width >= 0) - return sheet->default_width; - if ((sheet == NULL) || (sheet->cursor_style == NULL)) return DEFAULT_REGISTER_WIDTH; + if (sheet->default_width >= 0) + return sheet->default_width; + style = sheet->cursor_style[GNUCASH_CURSOR_HEADER]; if ((style == NULL) || (style->widths == NULL)) return DEFAULT_REGISTER_WIDTH; - sheet->default_width = gnucash_style_default_width(sheet, style); + sheet->default_width = gnucash_style_default_width (sheet, style); return sheet->default_width; } @@ -832,7 +836,7 @@ compute_optimal_height (GnucashSheet *sheet) return DEFAULT_REGISTER_HEIGHT; row_height = style->dimensions->pixel_heights[0][0]; - sheet->default_height = row_height * DEFAULT_REGISTER_ROWS; + sheet->default_height = row_height * gnucash_register_initial_rows; return sheet->default_height; } @@ -1049,6 +1053,9 @@ gnucash_sheet_delete_cb (GtkWidget *widget, int cursor_position = start_pos; int start_sel, end_sel; + if (end_pos <= start_pos) + return; + gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor), &p_row, &p_col); gnucash_cursor_get_virt (GNUCASH_CURSOR (sheet->cursor), @@ -1171,7 +1178,7 @@ gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet) sheet->insert_signal = gtk_signal_connect(GTK_OBJECT(sheet->entry), "insert_text", GTK_SIGNAL_FUNC(gnucash_sheet_insert_cb), - sheet); + sheet); sheet->delete_signal = gtk_signal_connect(GTK_OBJECT(sheet->entry), "delete_text", @@ -1179,8 +1186,6 @@ gnucash_sheet_start_editing_at_cursor (GnucashSheet *sheet) sheet); } -static gboolean dragging = FALSE; - static gboolean gnucash_motion_event (GtkWidget *widget, GdkEventMotion *event) { @@ -1194,14 +1199,20 @@ gnucash_motion_event (GtkWidget *widget, GdkEventMotion *event) sheet = GNUCASH_SHEET(widget); - if (!dragging || !sheet->editing || event->type != GDK_MOTION_NOTIFY) + if (!(event->state & GDK_BUTTON1_MASK) && sheet->grabbed) + { + gtk_grab_remove (widget); + sheet->grabbed = FALSE; + } + + if (sheet->button != 1) + return FALSE; + + if (!sheet->editing || event->type != GDK_MOTION_NOTIFY) return FALSE; if (!(event->state & GDK_BUTTON1_MASK)) - { - dragging = FALSE; return FALSE; - } gnome_canvas_get_scroll_offsets (GNOME_CANVAS(sheet), &xoffset, &yoffset); @@ -1221,14 +1232,28 @@ gnucash_motion_event (GtkWidget *widget, GdkEventMotion *event) static gboolean gnucash_button_release_event (GtkWidget *widget, GdkEventButton *event) { + GnucashSheet *sheet; + g_return_val_if_fail(widget != NULL, TRUE); g_return_val_if_fail(GNUCASH_IS_SHEET(widget), TRUE); g_return_val_if_fail(event != NULL, TRUE); + sheet = GNUCASH_SHEET (widget); + + if (sheet->button != event->button) + return FALSE; + + sheet->button = 0; + if (event->button != 1) return FALSE; - dragging = FALSE; + gtk_grab_remove (widget); + sheet->grabbed = FALSE; + + item_edit_set_has_selection(ITEM_EDIT(sheet->item_editor), FALSE); + + item_edit_claim_selection(ITEM_EDIT(sheet->item_editor), event->time); return TRUE; } @@ -1236,30 +1261,49 @@ gnucash_button_release_event (GtkWidget *widget, GdkEventButton *event) static void gnucash_sheet_scroll_event(GnucashSheet *sheet, GdkEventButton *event) { - GtkAdjustment *vadj; - gfloat multiplier = 1.0; - gfloat v_value; + GtkAdjustment *vadj; + gfloat multiplier = 1.0; + gfloat v_value; - vadj = sheet->vadj; - v_value = vadj->value; - if (event->state & GDK_SHIFT_MASK) - multiplier = 5.0; + vadj = sheet->vadj; + v_value = vadj->value; + if (event->state & GDK_SHIFT_MASK) + multiplier = 5.0; - switch (event->button) - { - case 4: - v_value -= vadj->step_increment * multiplier; - break; - case 5: - v_value += vadj->step_increment * multiplier; - break; - default: - return; - } + switch (event->button) + { + case 4: + v_value -= vadj->step_increment * multiplier; + break; + case 5: + v_value += vadj->step_increment * multiplier; + break; + default: + return; + } - v_value = CLAMP(v_value, vadj->lower, vadj->upper - vadj->page_size); + v_value = CLAMP(v_value, vadj->lower, vadj->upper - vadj->page_size); - gtk_adjustment_set_value(vadj, v_value); + gtk_adjustment_set_value(vadj, v_value); +} + +static void +gnucash_sheet_check_grab (GnucashSheet *sheet) +{ + GdkModifierType mods; + + if (!sheet->grabbed) + return; + + gdk_input_window_get_pointer(GTK_WIDGET(sheet)->window, + GDK_CORE_POINTER, NULL, NULL, + NULL, NULL, NULL, &mods); + + if (!(mods & GDK_BUTTON1_MASK)) + { + gtk_grab_remove (GTK_WIDGET(sheet)); + sheet->grabbed = FALSE; + } } static gboolean @@ -1286,11 +1330,24 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event) sheet = GNUCASH_SHEET (widget); table = sheet->table; + if (sheet->button && (sheet->button != event->button)) + return FALSE; + + sheet->button = event->button; + + if (!GTK_WIDGET_HAS_FOCUS(widget)) + gtk_widget_grab_focus(widget); + switch (event->button) { case 1: break; case 2: + if (event->type != GDK_BUTTON_PRESS) + return FALSE; + item_edit_paste_primary(ITEM_EDIT(sheet->item_editor), + event->time); + return TRUE; case 3: return FALSE; case 4: @@ -1332,13 +1389,19 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event) gtk_editable_set_position(GTK_EDITABLE(sheet->entry), -1); gtk_editable_select_region(GTK_EDITABLE(sheet->entry), 0, -1); + item_edit_claim_selection (ITEM_EDIT(sheet->item_editor), + event->time); + return TRUE; } if (event->type != GDK_BUTTON_PRESS) return FALSE; - dragging = TRUE; + gtk_grab_add(widget); + sheet->grabbed = TRUE; + + item_edit_set_has_selection(ITEM_EDIT(sheet->item_editor), TRUE); if ((new_p_row == current_p_row) && (new_p_col == current_p_col) && sheet->editing) @@ -1359,11 +1422,18 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event) GNC_TABLE_TRAVERSE_POINTER, &new_p_row, &new_p_col); + gnucash_sheet_check_grab(sheet); + if (exit_register) return TRUE; changed_cells = gnucash_sheet_cursor_move(sheet, new_p_row, new_p_col); + gnucash_sheet_check_grab(sheet); + + gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor), + &new_p_row, &new_p_col); + item_edit_set_cursor_pos (ITEM_EDIT(sheet->item_editor), new_p_row, new_p_col, x, changed_cells, FALSE); @@ -1371,20 +1441,67 @@ gnucash_button_press_event (GtkWidget *widget, GdkEventButton *event) return TRUE; } +void +gnucash_register_cut_clipboard (GnucashRegister *reg) +{ + GnucashSheet *sheet; + ItemEdit *item_edit; + + g_return_if_fail(reg != NULL); + g_return_if_fail(GNUCASH_IS_REGISTER(reg)); + + sheet = GNUCASH_SHEET(reg->sheet); + item_edit = ITEM_EDIT(sheet->item_editor); + + item_edit_cut_clipboard(item_edit, GDK_CURRENT_TIME); +} + +void +gnucash_register_copy_clipboard (GnucashRegister *reg) +{ + GnucashSheet *sheet; + ItemEdit *item_edit; + + g_return_if_fail(reg != NULL); + g_return_if_fail(GNUCASH_IS_REGISTER(reg)); + + sheet = GNUCASH_SHEET(reg->sheet); + item_edit = ITEM_EDIT(sheet->item_editor); + + item_edit_copy_clipboard(item_edit, GDK_CURRENT_TIME); +} + +void +gnucash_register_paste_clipboard (GnucashRegister *reg) +{ + GnucashSheet *sheet; + ItemEdit *item_edit; + + g_return_if_fail(reg != NULL); + g_return_if_fail(GNUCASH_IS_REGISTER(reg)); + + sheet = GNUCASH_SHEET(reg->sheet); + item_edit = ITEM_EDIT(sheet->item_editor); + + item_edit_paste_clipboard(item_edit, GDK_CURRENT_TIME); +} + static gboolean gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event) { ItemEdit *item_edit; gboolean handled = FALSE; + guint32 time; item_edit = ITEM_EDIT(sheet->item_editor); + time = event->time; switch (event->keyval) { case GDK_C: case GDK_c: if (event->state & GDK_CONTROL_MASK) { - item_edit_copy_clipboard(item_edit); + item_edit_copy_clipboard(item_edit, time); handled = TRUE; } break; @@ -1392,7 +1509,7 @@ gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event) case GDK_x: if (event->state & GDK_CONTROL_MASK) { - item_edit_cut_clipboard(item_edit); + item_edit_cut_clipboard(item_edit, time); handled = TRUE; } break; @@ -1400,19 +1517,19 @@ gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event) case GDK_v: if (event->state & GDK_CONTROL_MASK) { - item_edit_paste_clipboard(item_edit); + item_edit_paste_clipboard(item_edit, time); handled = TRUE; } break; case GDK_Insert: if (event->state & GDK_SHIFT_MASK) { - item_edit_paste_clipboard(item_edit); + item_edit_paste_clipboard(item_edit, time); handled = TRUE; } else if (event->state & GDK_CONTROL_MASK) { - item_edit_copy_clipboard(item_edit); + item_edit_copy_clipboard(item_edit, time); handled = TRUE; } break; @@ -1421,6 +1538,97 @@ gnucash_sheet_clipboard_event (GnucashSheet *sheet, GdkEventKey *event) return handled; } +static gncBoolean +gnucash_sheet_direct_event(GnucashSheet *sheet, GdkEvent *event) +{ + GtkEditable *editable; + Table *table = sheet->table; + int v_row, v_col, c_row, c_col; + int p_row, p_col; + gncBoolean result; + gboolean changed; + + const char *old_text; + char *new_text; + + int cursor_position, start_sel, end_sel; + int new_position, new_start, new_end; + + gnucash_cursor_get_phys(GNUCASH_CURSOR(sheet->cursor), &p_row, &p_col); + + gnucash_cursor_get_virt(GNUCASH_CURSOR(sheet->cursor), + &v_row, &v_col, &c_row, &c_col); + + if (!gnc_register_cell_valid (table, p_row, p_col, GNC_T)) + return GNC_F; + + old_text = gtk_entry_get_text (GTK_ENTRY(sheet->entry)); + if (old_text == NULL) + old_text = ""; + + new_text = (char *) old_text; + + editable = GTK_EDITABLE(sheet->entry); + + cursor_position = editable->current_pos; + start_sel = MIN(editable->selection_start_pos, + editable->selection_end_pos); + end_sel = MAX(editable->selection_start_pos, + editable->selection_end_pos); + + new_position = cursor_position; + new_start = start_sel; + new_end = end_sel; + + result = gnc_table_direct_update(table, + p_row, p_col, + old_text, &new_text, + &new_position, + &new_start, &new_end, + event); + + gnucash_sheet_cell_set_from_table (sheet, v_row, v_col, c_row, c_col); + + changed = FALSE; + + if ((new_text != old_text) && new_text != NULL) + { + gtk_signal_handler_block (GTK_OBJECT (sheet->entry), + sheet->insert_signal); + + gtk_signal_handler_block (GTK_OBJECT (sheet->entry), + sheet->delete_signal); + + gtk_entry_set_text (GTK_ENTRY (sheet->entry), new_text); + + gtk_signal_handler_unblock (GTK_OBJECT (sheet->entry), + sheet->delete_signal); + + gtk_signal_handler_unblock (GTK_OBJECT (sheet->entry), + sheet->insert_signal); + + changed = TRUE; + } + + if (new_position != cursor_position) + { + gtk_editable_set_position (editable, new_position); + changed = TRUE; + } + + if ((new_start != start_sel) || (new_end != end_sel)) + { + gtk_entry_select_region(GTK_ENTRY(sheet->entry), + new_start, new_end); + changed = TRUE; + } + + if (changed) + item_edit_redraw(ITEM_EDIT(sheet->item_editor)); + + return result; +} + static gint gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event) { @@ -1441,6 +1649,9 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event) table = sheet->table; header = table->handlers[0][0]; + if (gnucash_sheet_direct_event(sheet, (GdkEvent *) event)) + return TRUE; + gnucash_cursor_get_phys (GNUCASH_CURSOR(sheet->cursor), ¤t_p_row, ¤t_p_col); @@ -1485,7 +1696,7 @@ gnucash_sheet_key_press_event (GtkWidget *widget, GdkEventKey *event) break; case GDK_KP_Down: case GDK_Down: - if (event->state & GDK_CONTROL_MASK) + if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) { ItemEdit *item_edit; @@ -1830,6 +2041,8 @@ gnucash_sheet_cell_set_from_table (GnucashSheet *sheet, gint virt_row, gint virt_col, gint cell_row, gint cell_col) { SheetBlock *block; + SheetBlockStyle *style; + Table *table; gint p_row, p_col; gchar *text; @@ -1840,9 +2053,14 @@ gnucash_sheet_cell_set_from_table (GnucashSheet *sheet, gint virt_row, table = sheet->table; block = gnucash_sheet_get_block (sheet, virt_row, virt_col); + + style = block->style; + + if (!style) + return; - if (cell_row >= 0 && cell_row <= block->style->nrows - && cell_col >= 0 && cell_col <= block->style->ncols) { + if (cell_row >= 0 && cell_row <= style->nrows + && cell_col >= 0 && cell_col <= style->ncols) { if (block->entries[cell_row][cell_col]) g_free (block->entries[cell_row][cell_col]); @@ -1859,6 +2077,55 @@ gnucash_sheet_cell_set_from_table (GnucashSheet *sheet, gint virt_row, } + +gint +gnucash_sheet_col_max_width (GnucashSheet *sheet, gint virt_col, gint cell_col) +{ + int virt_row; + int cell_row; + int max = 0; + int width; + SheetBlock *block; + SheetBlockStyle *style; + GdkFont *font; + + g_return_val_if_fail (virt_col >= 0, 0); + g_return_val_if_fail (virt_col < sheet->num_virt_cols, 0); + g_return_val_if_fail (cell_col >= 0, 0); + + for (virt_row = 1; virt_row < sheet->num_virt_rows ; virt_row++) { + + block = gnucash_sheet_get_block (sheet, virt_row, virt_col); + style = block->style; + + if (!style) + continue; + + if (cell_col < style->ncols) + for (cell_row = 0; cell_row < style->nrows; cell_row++) { + const char *text = gnucash_sheet_block_get_text (sheet, + virt_row, virt_col, + cell_row, cell_col); + + if (style->fonts[cell_row][cell_col]) + font = style->fonts[cell_row][cell_col]; + else + font = GNUCASH_GRID(sheet->grid)->normal_font; + + if (!text || strlen(text) == 0) { + text = style->labels[cell_row][cell_col]; + font = style->header_font; + } + + width = gdk_string_measure (font, text) + 2*CELL_HPADDING; + max = MAX (max, width); + } + } + return max; +} + + + static void gnucash_sheet_block_destroy (GnucashSheet *sheet, gint virt_row, gint virt_col) { @@ -1954,7 +2221,7 @@ gnucash_sheet_table_load (GnucashSheet *sheet) Table *table; gint num_virt_rows; gint i, j; - + g_return_if_fail (sheet != NULL); g_return_if_fail (GNUCASH_IS_SHEET(sheet)); g_return_if_fail (sheet->table != NULL); @@ -1968,7 +2235,7 @@ gnucash_sheet_table_load (GnucashSheet *sheet) gnucash_sheet_resize (sheet); /* fill it up */ - for ( i = 0; i < table->num_virt_rows; i++) + for (i = 0; i < table->num_virt_rows; i++) for (j = 0; j < table->num_virt_cols; j++) gnucash_sheet_block_set_from_table (sheet, i, j); @@ -1982,6 +2249,53 @@ gnucash_sheet_table_load (GnucashSheet *sheet) } +static gboolean +gnucash_sheet_selection_clear (GtkWidget *widget, + GdkEventSelection *event) +{ + GnucashSheet *sheet; + + g_return_val_if_fail(widget != NULL, FALSE); + g_return_val_if_fail(GNUCASH_IS_SHEET(widget), FALSE); + + sheet = GNUCASH_SHEET(widget); + + return item_edit_selection_clear(ITEM_EDIT(sheet->item_editor), event); +} + +static void +gnucash_sheet_selection_get (GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GnucashSheet *sheet; + + g_return_if_fail(widget != NULL); + g_return_if_fail(GNUCASH_IS_SHEET(widget)); + + sheet = GNUCASH_SHEET(widget); + + item_edit_selection_get(ITEM_EDIT(sheet->item_editor), + selection_data, info, time); +} + +static void +gnucash_sheet_selection_received (GtkWidget *widget, + GtkSelectionData *selection_data, + guint time) +{ + GnucashSheet *sheet; + + g_return_if_fail(widget != NULL); + g_return_if_fail(GNUCASH_IS_SHEET(widget)); + + sheet = GNUCASH_SHEET(widget); + + item_edit_selection_received(ITEM_EDIT(sheet->item_editor), + selection_data, time); +} + static void gnucash_sheet_class_init (GnucashSheetClass *class) { @@ -1999,12 +2313,18 @@ gnucash_sheet_class_init (GnucashSheetClass *class) object_class->destroy = gnucash_sheet_destroy; widget_class->realize = gnucash_sheet_realize; + widget_class->size_request = gnucash_sheet_size_request; widget_class->size_allocate = gnucash_sheet_size_allocate; + widget_class->key_press_event = gnucash_sheet_key_press_event; widget_class->button_press_event = gnucash_button_press_event; widget_class->button_release_event = gnucash_button_release_event; widget_class->motion_notify_event = gnucash_motion_event; + + widget_class->selection_clear_event = gnucash_sheet_selection_clear; + widget_class->selection_received = gnucash_sheet_selection_received; + widget_class->selection_get = gnucash_sheet_selection_get; } @@ -2052,7 +2372,9 @@ gnucash_sheet_init (GnucashSheet *sheet) sheet->num_virt_cols = 0; sheet->item_editor = NULL; sheet->entry = NULL; - sheet->editing = FALSE; + sheet->editing = FALSE; + sheet->button = 0; + sheet->grabbed = FALSE; sheet->default_width = -1; sheet->default_height = -1; sheet->width = 0; @@ -2098,7 +2420,7 @@ gnucash_sheet_new (Table *table) GnomeCanvasGroup *sheet_group; g_return_val_if_fail (table != NULL, NULL); - + sheet = gnucash_sheet_create (table); /* FIXME: */ @@ -2119,7 +2441,6 @@ gnucash_sheet_new (Table *table) /* some register data */ sheet->layout_info_hash_table = g_hash_table_new (g_str_hash, g_str_equal); sheet->dimensions_hash_table = g_hash_table_new (g_str_hash, g_str_equal); - /* The cursor */ sheet->cursor = gnucash_cursor_new (sheet_group); @@ -2237,10 +2558,8 @@ gnucash_register_attach_popup(GnucashRegister *reg, GtkWidget *popup, g_return_if_fail(GNUCASH_IS_REGISTER(reg)); g_return_if_fail(GTK_IS_WIDGET(popup)); g_return_if_fail(reg->sheet != NULL); - g_return_if_fail(reg->header_canvas != NULL); gnome_popup_menu_attach(popup, reg->sheet, data); - gnome_popup_menu_attach(popup, reg->header_canvas, data); } @@ -2297,7 +2616,6 @@ gnucash_register_new (Table *table) reg->hscrollbar = scrollbar; gtk_widget_show(scrollbar); - return widget; } diff --git a/src/register/gnome/gnucash-sheet.h b/src/register/gnome/gnucash-sheet.h index 92117a7874..d4fb7df62b 100644 --- a/src/register/gnome/gnucash-sheet.h +++ b/src/register/gnome/gnucash-sheet.h @@ -170,6 +170,9 @@ typedef struct { gint editing; + gint button; /* mouse button being held down */ + gboolean grabbed; /* has the grab */ + guint insert_signal; guint delete_signal; guint changed_signal; @@ -206,6 +209,8 @@ void gnucash_sheet_set_top_block (GnucashSheet *sheet, int new_top_block, SheetBlock *gnucash_sheet_get_block (GnucashSheet *sheet, gint vrow, gint vcol); +gint +gnucash_sheet_col_max_width (GnucashSheet *sheet, gint virt_col, gint cell_col); gint gnucash_sheet_col_get_distance(GnucashSheet *sheet, int v_row, int col_a, int col_b); @@ -253,6 +258,12 @@ void gnucash_register_goto_next_virt_row (GnucashRegister *reg); void gnucash_register_attach_popup(GnucashRegister *reg, GtkWidget *popup, gpointer data); +void gnucash_register_set_initial_rows(guint num_rows); + +void gnucash_register_cut_clipboard (GnucashRegister *reg); +void gnucash_register_copy_clipboard (GnucashRegister *reg); +void gnucash_register_paste_clipboard (GnucashRegister *reg); + typedef struct { GnomeCanvasClass parent_class; diff --git a/src/register/gnome/gnucash-style.c b/src/register/gnome/gnucash-style.c index 6278896d68..dacb1373bd 100644 --- a/src/register/gnome/gnucash-style.c +++ b/src/register/gnome/gnucash-style.c @@ -277,14 +277,14 @@ gnucash_style_layout_init (GnucashSheet *sheet, SheetBlockStyle *style) case GNUCASH_CURSOR_SPLIT: { int i, j; - double perc[1][8] = {{0.10, 0.07, 0.15, 0.30, 0.02, 0.12, 0.12, 0.12}}; + double perc[1][8] = {{0.10, 0.07, 0.25, 0.20, 0.02, 0.12, 0.12, 0.12}}; CellLayoutData ld[1][8] = {{{STRING_FIXED, RESIZABLE, 0, 0,0,0,0,0,0,0,0,0, 0, 0, date_str}, {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL}, - {STRING_MIN | FILL, RESIZABLE, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, XFRM_STR}, {CHARS_MIN | FILL, RESIZABLE, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL}, + {STRING_MIN, RESIZABLE, 0, 0, 0 ,0, 10 ,0,0,0,0,0, 0, 0, XFRM_STR}, {STRING_FIXED, 0, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"}, - {CHARS_MIN | CHARS_MAX | FILL, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, + {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, }}; @@ -307,21 +307,21 @@ gnucash_style_layout_init (GnucashSheet *sheet, SheetBlockStyle *style) case GNUCASH_CURSOR_DOUBLE: { int i, j; - double perc[2][8] = {{0.10, 0.07, 0.15, 0.30, 0.02, 0.12, 0.12, 0.12}, - {0.10, 0.07, 0.15, 0.68, 0.0, 0.0, 0.0, 0.0}}; + double perc[2][8] = {{0.10, 0.07, 0.25, 0.20, 0.02, 0.12, 0.12, 0.12}, + {0.10, 0.07, 0.83, 0.0, 0.0, 0.0, 0.0, 0.0}}; CellLayoutData ld[2][8] = {{{STRING_FIXED, RESIZABLE, 0, 0,0,0,0,0,0,0,0,0, 0, 0, date_str}, {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL}, - {STRING_MIN | FILL, RESIZABLE, 0, 0, 0 ,0,0 ,0,0,0,0,0, 0, 0, XFRM_STR}, {CHARS_MIN | FILL, RESIZABLE, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL}, + {STRING_MIN, RESIZABLE, 0, 0, 0 ,0,0 ,0,0,0,0,0, 0, 0, XFRM_STR}, {STRING_FIXED, 0, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"}, - {CHARS_MIN | CHARS_MAX | FILL, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, + {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}}, {{LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,1,0,1, 0,0, NULL}, - {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,2,0,2, 0,0, NULL}, - {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,3,0,7, 0,0, NULL}, + {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,2,0,7, 0,0, NULL}, + {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, @@ -359,14 +359,14 @@ gnucash_style_layout_init (GnucashSheet *sheet, SheetBlockStyle *style) case GNUCASH_CURSOR_SPLIT: { int i, j; - double perc[1][11] = {{0.09, 0.06, 0.11, 0.23, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09}}; + double perc[1][11] = {{0.09, 0.06, 0.20, 0.14, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09}}; CellLayoutData ld[1][11] = {{{STRING_FIXED, RESIZABLE, 0, 0,0,0,0,0,0,0,0,0, 0, 0, date_str}, {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL}, - {STRING_MIN | FILL, RESIZABLE, 0, 0, 0 ,0, 0 ,0,0,0,0,0, 0, 0, XFRM_STR}, {CHARS_MIN | FILL, RESIZABLE, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL}, + {STRING_MIN, RESIZABLE, 0, 0, 0 ,0, 0 ,0,0,0,0,0, 0, 0, XFRM_STR}, {STRING_FIXED, 0, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"}, - {CHARS_MIN | CHARS_MAX | FILL, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, + {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, @@ -394,15 +394,15 @@ gnucash_style_layout_init (GnucashSheet *sheet, SheetBlockStyle *style) case GNUCASH_CURSOR_DOUBLE: { int i, j; - double perc[2][11] = {{0.09, 0.06, 0.11, 0.23, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09}, + double perc[2][11] = {{0.09, 0.06, 0.20, 0.14, 0.01, 0.10, 0.10, 0.07, 0.07, 0.07, 0.09}, {0.0, 0.15, 0.11, 0.74, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}; CellLayoutData ld[2][11] = {{{STRING_FIXED, RESIZABLE, 0, 0,0,0,0,0,0,0,0,0, 0, 0, date_str}, {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 3, 0, 5,0,0,0,0,0, 0, 0, NULL}, + {CHARS_MIN, RESIZABLE, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL}, {STRING_MIN | FILL, RESIZABLE, 0, 0, 0 ,0, 0 ,0,0,0,0,0, 0, 0, XFRM_STR}, - {CHARS_MIN | FILL, RESIZABLE, 0, 0,20,0,0,0,0,0,0,0, 0,0, NULL}, {STRING_FIXED, 0, 1, 0,0,0,0,0,0,0,0,0, 0, 0, "R"}, - {CHARS_MIN | CHARS_MAX | FILL, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, + {CHARS_MIN | CHARS_MAX, RESIZABLE, 0, 0, 9,0, 10,0,0,0,0,0, 0, 0, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}, @@ -410,8 +410,8 @@ gnucash_style_layout_init (GnucashSheet *sheet, SheetBlockStyle *style) {SAME_SIZE, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,5, NULL}}, {{LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,1,0,1, 0,0, NULL}, - {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,2,0,2, 0,0, NULL}, - {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,3,0,10, 0,0, NULL}, + {LEFT_ALIGNED|RIGHT_ALIGNED, RESIZABLE, 0, 0, 0,0,0,0,0,2,0,10, 0,0, NULL}, + {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, {PIXELS_FIXED, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0, NULL}, @@ -537,7 +537,7 @@ set_dimensions_pass_one (GnucashSheet *sheet, CellLayoutInfo *layout_info, g_return_if_fail (font != NULL); for (j = 0; j < layout_info->ncols; j++) { - dimensions->pixel_widths[i][j] = layout_info->cell_perc[i][j] * dimensions->width + 0.5; + dimensions->pixel_widths[i][j] = layout_info->cell_perc[i][j] * dimensions->width + 0.5; dimensions->pixel_heights[i][j] = font->ascent + font->descent + 2*CELL_VPADDING; @@ -724,7 +724,7 @@ set_dimensions_pass_three (GnucashSheet *sheet, CellLayoutInfo *layout_info, if (layout_info->flags[i][j] & PIXELS_MIN) { int allowed = dimensions->pixel_widths[i][j] - layout_info->pixels_min[i][j]; - adjustments[j] = MIN (allowed, cellspace); + adjustments[j] = MAX(0, MIN (allowed, cellspace)); done[j] = (allowed < cellspace); } else adjustments[j] = cellspace; @@ -733,8 +733,8 @@ set_dimensions_pass_three (GnucashSheet *sheet, CellLayoutInfo *layout_info, else { if (layout_info->flags[i][j] & PIXELS_MAX) { int allowed = dimensions->pixel_widths[i][j] - - layout_info->pixels_min[i][j]; - adjustments[j] = MAX (allowed, cellspace); + - layout_info->pixels_max[i][j]; + adjustments[j] = MIN(0, MAX (allowed, cellspace)); done[j] = (allowed >cellspace); } else adjustments[j] = cellspace; @@ -772,7 +772,7 @@ set_dimensions_pass_four(GnucashSheet *sheet, CellLayoutInfo *layout_info, int i = row, j; int c1, c2; int r; - + for (j = 0; j < layout_info->ncols; j++) { if (!(layout_info->flags[i][j] & (LEFT_ALIGNED | RIGHT_ALIGNED | PIXELS_FIXED))) { @@ -797,16 +797,52 @@ gnucash_style_default_width(GnucashSheet *sheet, SheetBlockStyle *style) { CellLayoutInfo *layout_info; CellDimensions *dimensions; + gint width; + gint i; layout_info = style->layout_info; dimensions = style->dimensions; - set_dimensions_pass_one(sheet, layout_info, dimensions, 0); - set_dimensions_pass_two(sheet, layout_info, dimensions, 0); + dimensions->height = 0; + width = dimensions->width = 0; - return compute_row_width(dimensions, 0, 0, layout_info->ncols); + /* Well, this is kind of wierd, isn't it. We do this five times + * because pass one and pass two interact in a strange way. Pass + * one sets widths based on percentages, and then pass two fixes + * them up based on layout info. If we only do this once, then + * when we get the allocation and we recompute them below, the + * pass one iteration messes up the pass two fixes. Then, when + * pass two is run again, the size gets bumped up to compensate. + * Running these 5 times is a hack to make the window come up + * in full horizontal view. */ + for (i = 0; i < 5; i++) + { + set_dimensions_pass_one(sheet, layout_info, dimensions, 0); + set_dimensions_pass_two(sheet, layout_info, dimensions, 0); + + width = compute_row_width(dimensions, 0, 0, + layout_info->ncols - 1); + + dimensions->width = width; + } + + return width; } + +gint +gnucash_style_row_width(SheetBlockStyle *style, int row) +{ + CellLayoutInfo *layout_info; + CellDimensions *dimensions; + + layout_info = style->layout_info; + dimensions = style->dimensions; + + return compute_row_width(dimensions, row, 0, layout_info->ncols - 1); +} + + static void compute_cell_origins_x (CellDimensions *dimensions, int row) { @@ -894,7 +930,8 @@ gnucash_sheet_style_set_dimensions (GnucashSheet *sheet, g_return_if_fail (GNUCASH_IS_SHEET (sheet)); g_return_if_fail (style != NULL); - style_recompute_layout_dimensions (sheet, style->layout_info, style->dimensions); + style_recompute_layout_dimensions (sheet, style->layout_info, + style->dimensions); } gint @@ -907,8 +944,8 @@ gnucash_style_col_is_resizable (SheetBlockStyle *style, int col) } /* - * Set width of a specified cell and set the PIXEL_FIXED flag. If - * same_size is TRUE, also set the width of any + * Set width of a specified cell and set the PIXEL_FIXED flag. + * If same_size is TRUE, also set the width of any * SAME_SIZE cell which references this one. Otherwise those * cells are set to PIXELS_FIXED with their current width. */ @@ -925,18 +962,12 @@ gnucash_sheet_style_set_col_width (GnucashSheet *sheet, SheetBlockStyle *style, if (width >= 0) { -#if 0 - if (style->dimensions->pixel_widths[0][col] > width) { - style->layout_info->pixels_max[0][col] = width; - style->layout_info->flags[0][col] = PIXELS_MAX; - } - else { - style->layout_info->pixels_min[0][col] = width; - style->layout_info->flags[0][col] = PIXELS_MIN; - } -#endif - style->layout_info->flags[0][col] = PIXELS_FIXED; style->layout_info->pixels_width[0][col] = width; + /* Note that we may want to preserve the FILL flag on + * this, but for now let's leave it off. + * style->layout_info->flags[0][col] = PIXELS_FIXED | + * (style->layout_info->flags[0][col] & FILL); */ + style->layout_info->flags[0][col] = PIXELS_FIXED; style->dimensions->pixel_widths[0][col] = width; for (i = 0; i < style->nrows; i++) { @@ -947,7 +978,12 @@ gnucash_sheet_style_set_col_width (GnucashSheet *sheet, SheetBlockStyle *style, if (same_size) style->dimensions->pixel_widths[i][j] = width; else + { style->layout_info->flags[i][j] = PIXELS_FIXED; + style->layout_info->pixels_width[i][j] = + style->dimensions->pixel_widths[i][j]; + } + } } } @@ -961,8 +997,6 @@ gnucash_sheet_style_set_col_width (GnucashSheet *sheet, SheetBlockStyle *style, } - - void gnucash_sheet_style_destroy (GnucashSheet *sheet, SheetBlockStyle *style) { @@ -991,14 +1025,16 @@ gnucash_sheet_style_destroy (GnucashSheet *sheet, SheetBlockStyle *style) style->layout_info->refcount--; if (style->layout_info->refcount == 0) { - g_hash_table_remove (sheet->layout_info_hash_table, style_get_key (style)); + g_hash_table_remove (sheet->layout_info_hash_table, + style_get_key (style)); style_layout_info_destroy (style->layout_info); } style->dimensions->refcount--; if (style->dimensions->refcount == 0) { - g_hash_table_remove (sheet->dimensions_hash_table, style_get_key (style)); + g_hash_table_remove (sheet->dimensions_hash_table, + style_get_key (style)); style_dimensions_destroy (style->dimensions); } @@ -1006,13 +1042,71 @@ gnucash_sheet_style_destroy (GnucashSheet *sheet, SheetBlockStyle *style) } +/* Recompiles the style information from the cellblock, without + * recomputing the layout info or the dimensions. WARNING: this + * function assumes that the space for the style info has been + * allocated already. */ +void +gnucash_sheet_style_recompile(SheetBlockStyle *style, CellBlock *cellblock, + SplitRegister *sr, gint cursor_type) +{ + gint i, j, type; + char *label; + + for (i = 0; i < style->nrows; i++) + for (j = 0; j < style->ncols; j++) { + type = cellblock->cell_types[i][j]; + + style->widths[i][j] = cellblock->widths[j]; + + style->fonts[i][j] = NULL; + style->header_font = gnucash_default_font; + + if (type > -1) + label = sr->header_label_cells[type]->value; + else if (cursor_type == GNUCASH_CURSOR_HEADER) + label = cellblock->cells[i][j]->value; + else + label = ""; + + g_free(style->labels[i][j]); + style->labels[i][j] = g_strdup(label); + + style->active_bg_color[i][j] = + gnucash_color_argb_to_gdk + (cellblock->active_bg_color); + + style->inactive_bg_color[i][j] = + gnucash_color_argb_to_gdk + (cellblock->passive_bg_color); + + switch (cellblock->alignments[j]) { + case ALIGN_RIGHT: + style->alignments[i][j] = + GTK_JUSTIFY_RIGHT; + break; + case ALIGN_CENTER: + style->alignments[i][j] = + GTK_JUSTIFY_CENTER; + break; + default: + case ALIGN_FILL: + case ALIGN_LEFT: + style->alignments[i][j] = + GTK_JUSTIFY_LEFT; + break; + } + } +} + + SheetBlockStyle * gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock, gint cursor_type) { - gint i, j; SheetBlockStyle *style; SplitRegister *sr; + gint i; g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (GNUCASH_IS_SHEET (sheet), NULL); @@ -1035,7 +1129,7 @@ gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock, style->active_bg_color = g_new0 (GdkColor **, cellblock->numRows); style->inactive_bg_color = g_new0 (GdkColor **, cellblock->numRows); style->labels = g_new0 (char **, cellblock->numRows); - + for ( i = 0; i < style->nrows; i++) { style->widths[i] = g_new0(gint, style->ncols); style->alignments[i] = g_new0 (GtkJustification, style->ncols); @@ -1045,42 +1139,7 @@ gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock, style->labels[i] = g_new0 (char *, style->ncols); } - for (i = 0; i < style->nrows; i++) - for (j = 0; j < style->ncols; j++) { - gint type = cellblock->cell_types[i][j]; - char *label; - - style->widths[i][j] = cellblock->widths[j]; - - style->fonts[i][j] = NULL; - style->header_font = gnucash_default_font; - - if (type > -1) - label = sr->header_label_cells[type]->value; - else if (cursor_type == GNUCASH_CURSOR_HEADER) - label = cellblock->cells[i][j]->value; - else - label = ""; - - style->labels[i][j] = g_strdup(label); - - style->active_bg_color[i][j] = gnucash_color_argb_to_gdk (cellblock->active_bg_color); - style->inactive_bg_color[i][j] = gnucash_color_argb_to_gdk (cellblock->passive_bg_color); - - switch (cellblock->alignments[j]) { - case ALIGN_RIGHT: - style->alignments[i][j] = GTK_JUSTIFY_RIGHT; - break; - case ALIGN_CENTER: - style->alignments[i][j] = GTK_JUSTIFY_CENTER; - break; - default: - case ALIGN_FILL: - case ALIGN_LEFT: - style->alignments[i][j] = GTK_JUSTIFY_LEFT; - break; - } - } + gnucash_sheet_style_recompile(style, cellblock, sr, cursor_type); gnucash_style_layout_init (sheet, style); gnucash_style_dimensions_init (sheet, style); diff --git a/src/register/gnome/gnucash-style.h b/src/register/gnome/gnucash-style.h index b5420a0f6e..efe00b7e2e 100644 --- a/src/register/gnome/gnucash-style.h +++ b/src/register/gnome/gnucash-style.h @@ -34,6 +34,8 @@ void gnucash_sheet_style_set_col_width (GnucashSheet *sheet, gint gnucash_style_default_width(GnucashSheet *sheet, SheetBlockStyle *style); +gint gnucash_style_row_width(SheetBlockStyle *style, int row); + void gnucash_sheet_style_set_dimensions (GnucashSheet *sheet, SheetBlockStyle *style); @@ -43,6 +45,11 @@ SheetBlockStyle * gnucash_sheet_style_compile (GnucashSheet *sheet, CellBlock *cellblock, gint cursor_type); +void gnucash_sheet_style_recompile (SheetBlockStyle *style, + CellBlock *cellblock, + SplitRegister *sr, + gint cursor_type); + SheetBlockStyle *gnucash_sheet_get_style (GnucashSheet *sheet, gint vrow, gint vcol); diff --git a/src/register/gnome/quickfillcell-gnome.c b/src/register/gnome/quickfillcell-gnome.c new file mode 100644 index 0000000000..4251b27c8c --- /dev/null +++ b/src/register/gnome/quickfillcell-gnome.c @@ -0,0 +1,101 @@ +/********************************************************************\ + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +\********************************************************************/ + +/* quickfillcell-gnome.c + * + * Implements gnome dependant quickfill cell functions. + */ + +#include "config.h" + +#include + +#include "quickfillcell.h" +#include "util.h" + + +static gncBoolean +QuickFillDirect (BasicCell *bcell, + const char *oldval, + char **newval_ptr, + int *cursor_position, + int *start_selection, + int *end_selection, + void *gui_data) +{ + QuickFillCell *cell = (QuickFillCell *) bcell; + GdkEventKey *event = gui_data; + QuickFill *match; + int prefix_len; + + if (event->type != GDK_KEY_PRESS) + return GNC_F; + + switch (event->keyval) { + case GDK_slash: + if (!(event->state & GDK_MOD1_MASK)) + return GNC_F; + break; + case GDK_Tab: + case GDK_ISO_Left_Tab: + if (!(event->state & GDK_CONTROL_MASK)) + return GNC_F; + break; + default: + return GNC_F; + } + + match = xaccGetQuickFillStrLen(cell->qfRoot, oldval, *cursor_position); + if (match == NULL) + return GNC_T; + + match = xaccGetQuickFillUniqueLen(match, &prefix_len); + if (match == NULL) + return GNC_T; + + if ((match->text != NULL) && + (strncmp(match->text, oldval, strlen(oldval)) == 0) && + (strcmp(match->text, oldval) != 0)) + { + *newval_ptr = strdup(match->text); + assert(*newval_ptr != NULL); + xaccSetBasicCellValue(bcell, *newval_ptr); + } + + *cursor_position += prefix_len; + *start_selection = *cursor_position; + *end_selection = -1; + + return GNC_T; +} + + +void +xaccQuickFillGUIInit (QuickFillCell *cell) +{ + if (cell == NULL) + return; + + cell->cell.direct_update = QuickFillDirect; +} + +/* =============== end of file =================== */ + +/* + Local Variables: + c-basic-offset: 8 + End: +*/ diff --git a/src/register/pricecell.c b/src/register/pricecell.c index 9d2b226dff..95c795acf5 100644 --- a/src/register/pricecell.c +++ b/src/register/pricecell.c @@ -38,7 +38,7 @@ static void PriceSetValue (BasicCell *, const char *); static char * xaccPriceCellPrintValue (PriceCell *cell); -/* set the color of the text to red, if teh value is negative */ +/* set the color of the text to red, if the value is negative */ /* hack alert -- the actual color should probably be configurable */ #define COLORIZE(cell,amt) { \ if (0.0 > amt) { \ @@ -79,7 +79,7 @@ PriceEnter (BasicCell *_cell, static const char * PriceMV (BasicCell *_cell, - const char * oldval, + const char *oldval, const char *change, const char *newval, int *cursor_position, @@ -96,21 +96,28 @@ PriceMV (BasicCell *_cell, decimal_point = lc->decimal_point[0]; /* accept the newval string if user action was delete, etc. */ - if (change) { - /* if change is a decimal point, then count decimal points */ - if (decimal_point == change[0]) { - int i, count=0; - for (i=0; 0 != newval[i]; i++) { - if (decimal_point == newval[i]) count ++; - } - if (1 < count) return NULL; - } else { - /* accept numeric, reject non-alpha edits */ - if (! (isdigit (change[0]))) return NULL; + if (change != NULL) + { + int i, count=0; + + for (i=0; 0 != change[i]; i++) + { + /* accept only numbers or a decimal point */ + if (!isdigit(change[i]) && (decimal_point != change[i])) + return NULL; + + if (decimal_point == change[i]) + count++; } + + for (i=0; 0 != oldval[i]; i++) + if (decimal_point == oldval[i]) + count++; + + if (1 < count) return NULL; } - /* parse the float pt value and store it */ + /* parse the float pt value and store it */ cell->amount = xaccParseAmount (newval, cell->monetary); SET ((&(cell->cell)), newval); return newval; @@ -131,11 +138,34 @@ PriceLeave (BasicCell *_cell, const char *val) return val; /* Otherwise, return the new one. */ + SET ((&(cell->cell)), newval); return strdup(newval); } /* ================================================ */ +static char * +PriceHelp(BasicCell *bcell) +{ + PriceCell *cell = (PriceCell *) bcell; + + if ((bcell->value != NULL) && (bcell->value[0] != 0)) + { + char *help_str; + + help_str = xaccPriceCellPrintValue(cell); + + return strdup(help_str); + } + + if (bcell->blank_help != NULL) + return strdup(bcell->blank_help); + + return NULL; +} + +/* ================================================ */ + PriceCell * xaccMallocPriceCell (void) { @@ -153,8 +183,9 @@ xaccInitPriceCell (PriceCell *cell) xaccInitBasicCell( &(cell->cell)); cell->amount = 0.0; - cell->blank_zero = 1; - cell->prt_format = strdup ("%m"); + cell->precision = 2; + cell->blank_zero = GNC_T; + cell->min_trail_zeros = 2; cell->monetary = GNC_T; SET ( &(cell->cell), ""); @@ -164,6 +195,7 @@ xaccInitPriceCell (PriceCell *cell) cell->cell.modify_verify = PriceMV; cell->cell.leave_cell = PriceLeave; cell->cell.set_value = PriceSetValue; + cell->cell.get_help_value = PriceHelp; } /* ================================================ */ @@ -172,7 +204,6 @@ void xaccDestroyPriceCell (PriceCell *cell) { cell->amount = 0.0; - free (cell->prt_format); cell->prt_format = 0x0; xaccDestroyBasicCell ( &(cell->cell)); } @@ -182,34 +213,26 @@ static char * xaccPriceCellPrintValue (PriceCell *cell) { static char buff[PRTBUF]; - char tmpfmt[PRTBUF]; - char tmpval[PRTBUF]; - char *monet; if (cell->blank_zero && DEQ(cell->amount, 0.0)) { strcpy(buff, ""); return buff; } - /* check for monetary-style format not natively supported by printf */ - /* hack alert -- this type of extended function should be abstracted - * out to a gnc_sprintf type function, however, this is much - * easier said than done */ - monet = strstr (cell->prt_format, "%m"); - if (monet) { - strcpy (tmpfmt, cell->prt_format); - monet = strstr (tmpfmt, "%m"); - *(monet+1) = 's'; - xaccSPrintAmount (tmpval, cell->amount, PRTSEP); - snprintf (buff, PRTBUF, tmpfmt, tmpval); - } else { - snprintf (buff, PRTBUF, cell->prt_format, cell->amount); - } + xaccSPrintAmountGeneral(buff, cell->amount, PRTSEP, cell->precision, + cell->monetary, cell->min_trail_zeros); return buff; } /* ================================================ */ +double +xaccGetPriceCellValue (PriceCell *cell) +{ + assert(cell != NULL); + + return cell->amount; +} void xaccSetPriceCellValue (PriceCell * cell, double amt) { @@ -226,13 +249,12 @@ void xaccSetPriceCellValue (PriceCell * cell, double amt) /* ================================================ */ -void xaccSetPriceCellFormat (PriceCell * cell, char * fmt) +void +xaccSetPriceCellPrecision (PriceCell *cell, int precision) { - if (cell->prt_format) free (cell->prt_format); - cell->prt_format = strdup (fmt); + assert(cell != NULL); - /* make sure that the cell is updated with the new format */ - xaccSetPriceCellValue (cell, cell->amount); + cell->precision = precision; } /* ================================================ */ @@ -240,11 +262,33 @@ void xaccSetPriceCellFormat (PriceCell * cell, char * fmt) void xaccSetPriceCellMonetary (PriceCell * cell, gncBoolean monetary) { + assert(cell != NULL); + cell->monetary = monetary; } /* ================================================ */ +void +xaccSetPriceCellMinTrailZeros (PriceCell * cell, int min_trail_zeros) +{ + assert(cell != NULL); + + cell->min_trail_zeros = min_trail_zeros; +} + +/* ================================================ */ + +void +xaccSetPriceCellBlankZero (PriceCell *cell, gncBoolean blank_zero) +{ + assert(cell != NULL); + + cell->blank_zero = blank_zero; +} + +/* ================================================ */ + void xaccSetDebCredCellValue (PriceCell * deb, PriceCell * cred, double amt) { diff --git a/src/register/pricecell.h b/src/register/pricecell.h index 9ada657b95..0bb9e075b7 100644 --- a/src/register/pricecell.h +++ b/src/register/pricecell.h @@ -53,12 +53,17 @@ #include "basiccell.h" #include "gnc-common.h" -typedef struct _PriceCell { +typedef struct _PriceCell +{ BasicCell cell; - double amount; /* the amount associated with this cell */ - short blank_zero; /* controls printing of zero values */ - char *prt_format; /* controls display of value; printf format */ - gncBoolean monetary; /* controls parsing of values */ + + double amount; /* the amount associated with this cell */ + + int precision; /* precision of printed values */ + int min_trail_zeros; /* minimum number of trailing zeros to print */ + + gncBoolean blank_zero; /* controls printing of zero values */ + gncBoolean monetary; /* controls parsing of values */ } PriceCell; /* installs a callback to handle price recording */ @@ -66,17 +71,22 @@ PriceCell * xaccMallocPriceCell (void); void xaccInitPriceCell (PriceCell *); void xaccDestroyPriceCell (PriceCell *); -/* updates amount, string format is three decimal places */ -void xaccSetPriceCellValue (PriceCell *, double amount); +/* return the value of a price cell */ +double xaccGetPriceCellValue (PriceCell *cell); -/* The xaccSetPriceCellFormat() method is used to control how - * the cell contents are displayed. It accepts as an argument - * a printf-style format. The format must control the display - * of a double-precision float. See the printf() command for - * allowed syntax. The default format is "%m" for a monetary - * style format. - */ -void xaccSetPriceCellFormat (PriceCell *, char * fmt); +/* updates amount, string format is three decimal places */ +void xaccSetPriceCellValue (PriceCell *cell, double amount); + +/* sets the precision of the printed value. Defaults to 2 */ +void xaccSetPriceCellPrecision (PriceCell *cell, int precision); + +/* Sets the mininum number of trailing decimal zeros that must + * be printed. Defaults to 2. */ +void xaccSetPriceCellMinTrailZeros (PriceCell *cell, int); + +/* determines whether 0 values are left blank or printed. + * defaults to true. */ +void xaccSetPriceCellBlankZero (PriceCell *cell, gncBoolean); /* The xaccSetPriceCellMonetary() sets a flag which determines * how string amounts are parsed, either as monetary or @@ -88,7 +98,7 @@ void xaccSetPriceCellMonetary (PriceCell *, gncBoolean); * the credit cell if amount is positive, and makes the other cell * blank. */ void xaccSetDebCredCellValue (PriceCell *deb, - PriceCell *cred, double amount); + PriceCell *cred, double amount); #endif /* __XACC_PRICE_CELL_C__ */ diff --git a/src/register/quickfillcell-motif.c b/src/register/quickfillcell-motif.c new file mode 100644 index 0000000000..e2dacc7074 --- /dev/null +++ b/src/register/quickfillcell-motif.c @@ -0,0 +1,29 @@ +/********************************************************************\ + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +\********************************************************************/ + +/* quickfillcell-motif.c + * + * Implements motif dependent quick fill cell functions. + */ + +#include "quickfillcell.h" + + +void +xaccQuickFillGUIInit (QuickFillCell *cell) +{ + /* does nothing */ +} diff --git a/src/register/quickfillcell.c b/src/register/quickfillcell.c index 7c8753affe..4599a9ebed 100644 --- a/src/register/quickfillcell.c +++ b/src/register/quickfillcell.c @@ -257,6 +257,8 @@ xaccInitQuickFillCell (QuickFillCell *cell) cell->cell.modify_verify = quick_modify; cell->cell.leave_cell = quick_leave; cell->cell.set_value = quick_set; + + xaccQuickFillGUIInit (cell); } /* ================================================ */ diff --git a/src/register/quickfillcell.h b/src/register/quickfillcell.h index de24196c26..779ccb4fc9 100644 --- a/src/register/quickfillcell.h +++ b/src/register/quickfillcell.h @@ -61,6 +61,9 @@ void xaccDestroyQuickFillCell (QuickFillCell *); void xaccSetQuickFillCellValue (QuickFillCell *, const char *); void xaccSetQuickFillSort (QuickFillCell *, QuickFillSort); +/* GUI-dependent */ +void xaccQuickFillGUIInit (QuickFillCell *); + #endif /* __XACC_FILL_CELL_C__ */ /* --------------- end of file ---------------------- */ diff --git a/src/register/splitreg.c b/src/register/splitreg.c index cd35663b6c..70722a653e 100644 --- a/src/register/splitreg.c +++ b/src/register/splitreg.c @@ -43,6 +43,27 @@ /* This static indicates the debugging module that this .o belongs to. */ static short module = MOD_REGISTER; +static SplitRegisterColors reg_colors = { + 0xffdddd, /* pale red, single cursor active */ + 0xccccff, /* pale blue, single cursor passive */ + 0xccccff, /* pale blue, single cursor passive 2 */ + + 0xffdddd, /* pale red, double cursor active */ + 0xccccff, /* pale blue, double cursor passive */ + 0xffffff, /* white, double cursor passive 2 */ + + GNC_F, /* double mode alternate by physical row */ + + 0xffdddd, /* pale red, trans cursor active */ + 0xccccff, /* pale blue, trans cursor passive */ + + 0xffffdd, /* pale yellow, split cursor active */ + 0xffffff, /* white, split cursor passive */ + + 0xffffff /* white, header color */ +}; + + /* utility defines for cell configuration data */ #define DATE_CELL 0 #define NUM_CELL 1 @@ -89,9 +110,9 @@ static short module = MOD_REGISTER; #define DATE_CELL_ALIGN ALIGN_RIGHT #define NUM_CELL_ALIGN ALIGN_LEFT #define ACTN_CELL_ALIGN ALIGN_LEFT -#define XFRM_CELL_ALIGN ALIGN_LEFT -#define MXFRM_CELL_ALIGN ALIGN_LEFT -#define XTO_CELL_ALIGN ALIGN_LEFT +#define XFRM_CELL_ALIGN ALIGN_RIGHT +#define MXFRM_CELL_ALIGN ALIGN_RIGHT +#define XTO_CELL_ALIGN ALIGN_RIGHT #define DESC_CELL_ALIGN ALIGN_LEFT #define MEMO_CELL_ALIGN ALIGN_LEFT #define RECN_CELL_ALIGN ALIGN_CENTER @@ -104,14 +125,7 @@ static short module = MOD_REGISTER; #define SHRS_CELL_ALIGN ALIGN_RIGHT #define BALN_CELL_ALIGN ALIGN_RIGHT -/* Use 4 decimal places for everything that isn't a $ value - * Perhaps this should be changed to store precision selection - * in a config file. */ -#define SHRS_CELL_FORMAT "%0.4f" -#define DEBIT_CELL_FORMAT "%0.4f" -#define CREDIT_CELL_FORMAT "%0.4f" -#define PRICE_CELL_FORMAT "%0.4f" - +#define SHARES_PRECISION 4 /* ============================================== */ @@ -357,27 +371,29 @@ configLayout (SplitRegister *reg) curs = reg->double_cursor; FANCY (DATE, date, 0, 0); BASIC (NUM, num, 1, 0); - FANCY (DESC, desc, 3, 0); + FANCY (DESC, desc, 2, 0); + FANCY (MXFRM, mxfrm, 3, 0); BASIC (RECN, recn, 4, 0); FANCY (DEBT, debit, 5, 0); FANCY (CRED, credit, 6, 0); FANCY (BALN, balance, 7, 0); FANCY (ACTN, action, 1, 1); - FANCY (MXFRM, mxfrm, 2, 1); - BASIC (MEMO, memo, 3, 1); + FANCY (MEMO, memo, 2, 1); curs = reg->trans_cursor; FANCY (DATE, date, 0, 0); BASIC (NUM, num, 1, 0); - FANCY (DESC, desc, 3, 0); + FANCY (DESC, desc, 2, 0); BASIC (RECN, recn, 4, 0); + FANCY (DEBT, debit, 5, 0); + FANCY (CRED, credit, 6, 0); FANCY (BALN, balance, 7, 0); curs = reg->split_cursor; FANCY (ACTN, action, 1, 0); - FANCY (XFRM, xfrm, 2, 0); - BASIC (MEMO, memo, 3, 0); + FANCY (MEMO, memo, 2, 0); + FANCY (XFRM, xfrm, 3, 0); FANCY (NDEBT, ndebit, 5, 0); FANCY (NCRED, ncredit, 6, 0); @@ -385,8 +401,8 @@ configLayout (SplitRegister *reg) curs = reg->single_cursor; FANCY (DATE, date, 0, 0); BASIC (NUM, num, 1, 0); - FANCY (MXFRM, mxfrm, 2, 0); - FANCY (DESC, desc, 3, 0); + FANCY (DESC, desc, 2, 0); + FANCY (MXFRM, mxfrm, 3, 0); BASIC (RECN, recn, 4, 0); FANCY (DEBT, debit, 5, 0); FANCY (CRED, credit, 6, 0); @@ -404,7 +420,8 @@ configLayout (SplitRegister *reg) curs = reg->double_cursor; FANCY (DATE, date, 0, 0); BASIC (NUM, num, 1, 0); - FANCY (DESC, desc, 3, 0); + FANCY (DESC, desc, 2, 0); + FANCY (MXFRM, mxfrm, 3, 0); BASIC (RECN, recn, 4, 0); FANCY (DEBT, debit, 5, 0); FANCY (CRED, credit, 6, 0); @@ -414,14 +431,13 @@ configLayout (SplitRegister *reg) FANCY (BALN, balance, 10, 0); FANCY (ACTN, action, 1, 1); - FANCY (MXFRM, mxfrm, 2, 1); - BASIC (MEMO, memo, 3, 1); + FANCY (MEMO, memo, 2, 1); /* only the transaction cursor gets used */ curs = reg->trans_cursor; FANCY (DATE, date, 0, 0); BASIC (NUM, num, 1, 0); - FANCY (DESC, desc, 3, 0); + FANCY (DESC, desc, 2, 0); BASIC (RECN, recn, 4, 0); FANCY (DEBT, debit, 5, 0); FANCY (CRED, credit, 6, 0); @@ -432,8 +448,8 @@ configLayout (SplitRegister *reg) curs = reg->split_cursor; FANCY (ACTN, action, 1, 0); - FANCY (XFRM, xfrm, 2, 0); - BASIC (MEMO, memo, 3, 0); + FANCY (MEMO, memo, 2, 0); + FANCY (XFRM, xfrm, 3, 0); FANCY (NDEBT, ndebit, 5, 0); FANCY (NCRED, ncredit, 6, 0); @@ -441,8 +457,8 @@ configLayout (SplitRegister *reg) curs = reg->single_cursor; FANCY (DATE, date, 0, 0); BASIC (NUM, num, 1, 0); - FANCY (MXFRM, mxfrm, 2, 0); - FANCY (DESC, desc, 3, 0); + FANCY (DESC, desc, 2, 0); + FANCY (MXFRM, mxfrm, 3, 0); BASIC (RECN, recn, 4, 0); FANCY (DEBT, debit, 5, 0); FANCY (CRED, credit, 6, 0); @@ -700,7 +716,8 @@ configTraverse (SplitRegister *reg) /* ============================================== */ -SplitRegister * xaccMallocSplitRegister (int type) +SplitRegister * +xaccMallocSplitRegister (int type) { SplitRegister * reg; reg = (SplitRegister *) malloc (sizeof (SplitRegister)); @@ -710,39 +727,83 @@ SplitRegister * xaccMallocSplitRegister (int type) /* ============================================== */ +void +xaccSetSplitRegisterColors (SplitRegisterColors reg_colors_new) +{ + reg_colors = reg_colors_new; +} + +/* ============================================== */ + +static void +configTable(SplitRegister *reg) +{ + int style = reg->type & REG_STYLE_MASK; + + if ((reg == NULL) || (reg->table == NULL)) + return; + + switch (style) { + case REG_SINGLE_LINE: + case REG_SINGLE_DYNAMIC: + reg->table->alternate_bg_colors = GNC_T; + break; + case REG_DOUBLE_LINE: + case REG_DOUBLE_DYNAMIC: + reg->table->alternate_bg_colors = reg_colors.double_alternate_virt; + break; + default: + reg->table->alternate_bg_colors = GNC_F; + break; + } +} + +/* ============================================== */ + +void +xaccSplitRegisterConfigColors (SplitRegister *reg) +{ + reg->single_cursor->active_bg_color = + reg_colors.single_cursor_active_bg_color; + reg->single_cursor->passive_bg_color = + reg_colors.single_cursor_passive_bg_color; + reg->single_cursor->passive_bg_color2 = + reg_colors.single_cursor_passive_bg_color2; + + reg->double_cursor->active_bg_color = + reg_colors.double_cursor_active_bg_color; + reg->double_cursor->passive_bg_color = + reg_colors.double_cursor_passive_bg_color; + reg->double_cursor->passive_bg_color2 = + reg_colors.double_cursor_passive_bg_color2; + + reg->trans_cursor->active_bg_color = + reg_colors.trans_cursor_active_bg_color; + reg->trans_cursor->passive_bg_color = + reg_colors.trans_cursor_passive_bg_color; + reg->trans_cursor->passive_bg_color2 = + reg_colors.trans_cursor_passive_bg_color; + + reg->split_cursor->active_bg_color = + reg_colors.split_cursor_active_bg_color; + reg->split_cursor->passive_bg_color = + reg_colors.split_cursor_passive_bg_color; + reg->split_cursor->passive_bg_color2 = + reg_colors.split_cursor_passive_bg_color; + + reg->header->active_bg_color = reg_colors.header_bg_color; + reg->header->passive_bg_color = reg_colors.header_bg_color; + reg->header->passive_bg_color2 = reg_colors.header_bg_color; + + configTable(reg); +} + +/* ============================================== */ + static void configCursors (SplitRegister *reg) { - /* --------------------------- */ - /* set the color of the cells in the cursors */ - /* hack alert -- the actual color should depend on the - * type of register. */ -/* they used to be the following, once upon a time: - "*regbank.oddRowBackground: #aaccff", - "*regcash.oddRowBackground: #ccffcc", - "*regasset.oddRowBackground: #aaffcc", - "*regcredit.oddRowBackground: #ffffaa", - "*regliability.oddRowBackground: #ffcccc", - "*ledportfolio.oddRowBackground: #ccffff", - "*regmutual.oddRowBackground: #ccffff", - "*regincome.oddRowBackground: #aaccff", - "*regexpense.oddRowBackground: #ffcccc", - "*regequity.oddRowBackground: #ffffaa", - "*ledgportfolio.evenRowBackground:grey", - "*regmutual.evenRowBackground: grey", -*/ - reg->single_cursor->active_bg_color = 0xffdddd; /* pale red */ - reg->single_cursor->passive_bg_color = 0xccccff; /* pale blue */ - - reg->double_cursor->active_bg_color = 0xffdddd; /* pale red */ - reg->double_cursor->passive_bg_color = 0xccccff; /* pale blue */ - - reg->trans_cursor->active_bg_color = 0xffdddd; /* pale red */ - reg->trans_cursor->passive_bg_color = 0xccccff; /* pale blue */ - - reg->split_cursor->active_bg_color = 0xffffdd; /* pale yellow */ - reg->split_cursor->passive_bg_color = 0xffffff; /* white */ - + xaccSplitRegisterConfigColors(reg); } /* ============================================== */ @@ -808,6 +869,7 @@ xaccInitSplitRegister (SplitRegister *reg, int type) CellBlock *header; int phys_r, phys_c; + reg->table = NULL; reg->user_data = NULL; reg->destroy = NULL; reg->type = type; @@ -835,7 +897,7 @@ xaccInitSplitRegister (SplitRegister *reg, int type) HDR (VALU); HDR (SHRS); HDR (BALN); - + HDR (NCRED); HDR (NDEBT); @@ -854,7 +916,7 @@ xaccInitSplitRegister (SplitRegister *reg, int type) NEW (mxfrm, Combo); NEW (xto, Combo); NEW (action, Combo); - NEW (memo, Text); + NEW (memo, QuickFill); NEW (credit, Price); NEW (debit, Price); NEW (price, Price); @@ -885,9 +947,24 @@ xaccInitSplitRegister (SplitRegister *reg, int type) * cells handles this for us. */ - reg -> nullCell -> input_output = XACC_CELL_ALLOW_NONE; + reg->nullCell->input_output = XACC_CELL_ALLOW_NONE; xaccSetBasicCellValue (reg->nullCell, ""); + /* The num cell is the transaction number */ + xaccSetBasicCellBlankHelp (reg->numCell, NUM_CELL_HELP); + + /* the xfer cells */ + xaccSetBasicCellBlankHelp (®->mxfrmCell->cell, XFER_CELL_HELP); + xaccSetBasicCellBlankHelp (®->xfrmCell->cell, XFER_CELL_HELP); + xaccComboCellSetIgnoreString (reg->mxfrmCell, SPLIT_STR); + xaccComboCellSetIgnoreHelp (reg->mxfrmCell, TOOLTIP_MULTI_SPLIT); + + /* the memo cell */ + xaccSetBasicCellBlankHelp (®->memoCell->cell, MEMO_CELL_HELP); + + /* the desc cell */ + xaccSetBasicCellBlankHelp (®->descCell->cell, DESC_CELL_HELP); + /* balance cell does not accept input; its display only. */ /* however, we *do* want it to shadow the true cell contents when * the cursor is repositioned. Othewise, it will just display @@ -901,17 +978,7 @@ xaccInitSplitRegister (SplitRegister *reg, int type) */ reg->recnCell->input_output |= XACC_CELL_ALLOW_EXACT_ONLY; - /* the debit/credit/value cells show blank if value is 0.00 */ - reg->debitCell->blank_zero = 1; - reg->creditCell->blank_zero = 1; - reg->valueCell->blank_zero = 1; - reg->ndebitCell->blank_zero = 1; - reg->ncreditCell->blank_zero = 1; - - /* ok, now make sure the initail value of 0.0 is blanked. - * if this is not done, then various oddball situations - * will show the non-blanked values. - */ + /* Initialize price cells */ xaccSetPriceCellValue (reg->debitCell, 0.0); xaccSetPriceCellValue (reg->creditCell, 0.0); xaccSetPriceCellValue (reg->valueCell, 0.0); @@ -920,27 +987,31 @@ xaccInitSplitRegister (SplitRegister *reg, int type) /* The format for share-related info is a printf-style * format string for a double. */ - xaccSetPriceCellFormat (reg->shrsCell, SHRS_CELL_FORMAT); - xaccSetPriceCellMonetary (reg->shrsCell, GNC_F); + xaccSetPriceCellMinTrailZeros (reg->shrsCell, 0); + xaccSetPriceCellPrecision (reg->shrsCell, SHARES_PRECISION); /* The action cell should accept strings not in the list */ xaccComboCellSetStrict (reg->actionCell, GNC_F); + xaccSetBasicCellBlankHelp (®->actionCell->cell, ACTION_CELL_HELP); /* number format for share quantities in stock ledgers */ switch (type & REG_TYPE_MASK) { case STOCK_REGISTER: case PORTFOLIO: case CURRENCY_REGISTER: - xaccSetPriceCellFormat (reg->debitCell, DEBIT_CELL_FORMAT); - xaccSetPriceCellFormat (reg->creditCell, CREDIT_CELL_FORMAT); - xaccSetPriceCellFormat (reg->ndebitCell, DEBIT_CELL_FORMAT); - xaccSetPriceCellFormat (reg->ncreditCell, CREDIT_CELL_FORMAT); - xaccSetPriceCellFormat (reg->priceCell, DEBIT_CELL_FORMAT); - xaccSetPriceCellMonetary (reg->debitCell, GNC_F); - xaccSetPriceCellMonetary (reg->creditCell, GNC_F); - xaccSetPriceCellMonetary (reg->ndebitCell, GNC_F); - xaccSetPriceCellMonetary (reg->ncreditCell, GNC_F); - xaccSetPriceCellMonetary (reg->priceCell, GNC_F); + xaccSetPriceCellMinTrailZeros (reg->debitCell, 0); + xaccSetPriceCellMinTrailZeros (reg->creditCell, 0); + xaccSetPriceCellMinTrailZeros (reg->ndebitCell, 0); + xaccSetPriceCellMinTrailZeros (reg->ncreditCell, 0); + + xaccSetPriceCellPrecision (reg->debitCell, SHARES_PRECISION); + xaccSetPriceCellPrecision (reg->creditCell, SHARES_PRECISION); + xaccSetPriceCellPrecision (reg->ndebitCell, SHARES_PRECISION); + xaccSetPriceCellPrecision (reg->ncreditCell, SHARES_PRECISION); + xaccSetPriceCellPrecision (reg->priceCell, SHARES_PRECISION); + + xaccSetBasicCellBlankHelp (®->priceCell->cell, PRICE_CELL_HELP); + xaccSetBasicCellBlankHelp (®->valueCell->cell, VALUE_CELL_HELP); break; default: break; @@ -958,6 +1029,7 @@ xaccInitSplitRegister (SplitRegister *reg, int type) /* -------------------------------- */ phys_r = header->numRows; + reg->cursor_phys_col = 0; reg->cursor_phys_row = phys_r; /* cursor on first line past header */ reg->cursor_virt_row = 1; @@ -986,6 +1058,8 @@ xaccInitSplitRegister (SplitRegister *reg, int type) xaccMoveCursor (table, header->numRows, 0); reg->table = table; + + configTable(reg); } /* ============================================== */ @@ -997,12 +1071,14 @@ xaccConfigSplitRegister (SplitRegister *reg, int newtype) reg->type = newtype; - /* Make sure that any GU elemnts associated with this reconfig + /* Make sure that any GUI elements associated with this reconfig * are properly initialized. */ xaccCreateCursor (reg->table, reg->single_cursor); xaccCreateCursor (reg->table, reg->double_cursor); xaccCreateCursor (reg->table, reg->trans_cursor); xaccCreateCursor (reg->table, reg->split_cursor); + + configTable(reg); } /* ============================================== */ @@ -1042,7 +1118,7 @@ xaccDestroySplitRegister (SplitRegister *reg) xaccDestroyComboCell (reg->xfrmCell); xaccDestroyComboCell (reg->mxfrmCell); xaccDestroyComboCell (reg->xtoCell); - xaccDestroyBasicCell (reg->memoCell); + xaccDestroyQuickFillCell (reg->memoCell); xaccDestroyPriceCell (reg->creditCell); xaccDestroyPriceCell (reg->debitCell); xaccDestroyPriceCell (reg->priceCell); @@ -1093,7 +1169,7 @@ xaccSplitRegisterGetChangeFlag (SplitRegister *reg) changed |= MOD_XFRM & reg->xfrmCell->cell.changed; changed |= MOD_MXFRM & reg->mxfrmCell->cell.changed; changed |= MOD_XTO & reg->xtoCell->cell.changed; - changed |= MOD_MEMO & reg->memoCell->changed; + changed |= MOD_MEMO & reg->memoCell->cell.changed; changed |= MOD_AMNT & reg->creditCell->cell.changed; changed |= MOD_AMNT & reg->debitCell->cell.changed; changed |= MOD_PRIC & reg->priceCell->cell.changed; @@ -1119,7 +1195,7 @@ xaccSplitRegisterClearChangeFlag (SplitRegister *reg) reg->xfrmCell->cell.changed = 0; reg->mxfrmCell->cell.changed = 0; reg->xtoCell->cell.changed = 0; - reg->memoCell->changed = 0; + reg->memoCell->cell.changed = 0; reg->creditCell->cell.changed = 0; reg->debitCell->cell.changed = 0; reg->priceCell->cell.changed = 0; diff --git a/src/register/splitreg.h b/src/register/splitreg.h index 480a9d7010..e834f744ed 100644 --- a/src/register/splitreg.h +++ b/src/register/splitreg.h @@ -143,7 +143,7 @@ struct _SplitRegister { ComboCell * xfrmCell; ComboCell * mxfrmCell; ComboCell * xtoCell; - BasicCell * memoCell; + QuickFillCell * memoCell; PriceCell * creditCell; PriceCell * debitCell; PriceCell * priceCell; @@ -177,14 +177,41 @@ struct _SplitRegister { /* The destroy callback gives user's a chance * to free up any associated user_hook data */ void (* destroy) (SplitRegister *); - }; + +typedef struct _SplitRegisterColors SplitRegisterColors; + +struct _SplitRegisterColors +{ + uint32 single_cursor_active_bg_color; + uint32 single_cursor_passive_bg_color; + uint32 single_cursor_passive_bg_color2; + + uint32 double_cursor_active_bg_color; + uint32 double_cursor_passive_bg_color; + uint32 double_cursor_passive_bg_color2; + + gncBoolean double_alternate_virt; + + uint32 trans_cursor_active_bg_color; + uint32 trans_cursor_passive_bg_color; + + uint32 split_cursor_active_bg_color; + uint32 split_cursor_passive_bg_color; + + uint32 header_bg_color; +}; + + SplitRegister * xaccMallocSplitRegister (int type); void xaccInitSplitRegister (SplitRegister *, int type); void xaccConfigSplitRegister (SplitRegister *, int type); void xaccDestroySplitRegister (SplitRegister *); +void xaccSetSplitRegisterColors (SplitRegisterColors reg_colors); +void xaccSplitRegisterConfigColors (SplitRegister *reg); + /* returns non-zero value if updates have been made to data */ unsigned int xaccSplitRegisterGetChangeFlag (SplitRegister *); diff --git a/src/register/table-allgui.c b/src/register/table-allgui.c index 83baa609bf..ab2defba6c 100644 --- a/src/register/table-allgui.c +++ b/src/register/table-allgui.c @@ -75,6 +75,7 @@ xaccInitTable (Table * table) table->move_cursor = NULL; table->traverse = NULL; + table->set_help = NULL; table->client_data = NULL; table->entries = NULL; @@ -85,6 +86,8 @@ xaccInitTable (Table * table) table->user_data = NULL; table->handlers = NULL; + table->alternate_bg_colors = GNC_F; + /* invalidate the "previous" traversed cell */ table->prev_phys_traverse_row = -1; table->prev_phys_traverse_col = -1; @@ -376,7 +379,7 @@ makePassive (Table *table) int phys_row = table->current_cursor_phys_row; int phys_col = table->current_cursor_phys_col; int r_origin, c_origin; - + int virt_row; /* Change the cell background colors to their "passive" values. * This denotes that the cursor has left this location (which means more or @@ -390,21 +393,27 @@ makePassive (Table *table) r_origin -= table->locators[phys_row][phys_col]->phys_row_offset; c_origin -= table->locators[phys_row][phys_col]->phys_col_offset; + virt_row = table->locators[phys_row][phys_col]->virt_row; + curs = table->current_cursor; for (i=0; inumRows; i++) { for (j=0; jnumCols; j++) { BasicCell *cell; - -/* yooooo hack alert -- the color capabilities for the cursor should - * be per-cell, not per cursor; so we do a quickie hack ughhh. - * first line is whatever was speced, the second line is white. - */ -if (0==i) { - table->bg_colors[i+r_origin][j+c_origin] = curs->passive_bg_color; -} else { -table->bg_colors[i+r_origin][j+c_origin] = 0xffffff; -} + uint32 color; + + if (table->alternate_bg_colors) { + if ((virt_row % 2) == 1) + color = curs->passive_bg_color; + else + color = curs->passive_bg_color2; + } + else if (0 == i) + color = curs->passive_bg_color; + else + color = curs->passive_bg_color2; + + table->bg_colors[i+r_origin][j+c_origin] = color; cell = curs->cells[i][j]; if (cell) { @@ -935,14 +944,29 @@ gnc_table_enter_update(Table *table, int row, int col, int *cursor_position, * different, freeing the old one. (Doing a strcmp would leak memory). */ if (retval && (val != retval)) { + if (safe_strcmp(retval, val) != 0) + (arr->cells[rel_row][rel_col])->changed = 0xffffffff; if (table->entries[row][col]) free (table->entries[row][col]); table->entries[row][col] = retval; - (arr->cells[rel_row][rel_col])->changed = 0xffffffff; } else { retval = NULL; } } + if (table->set_help) + { + BasicCell *cell; + char *help_str; + + cell = arr->cells[rel_row][rel_col]; + help_str = xaccBasicCellGetHelp(cell); + + table->set_help(table, help_str, table->client_data); + + if (help_str != NULL) + free(help_str); + } + /* record this position as the cell that will be * traversed out of if a traverse even happens */ table->prev_phys_traverse_row = row; @@ -1017,7 +1041,8 @@ gnc_table_leave_update(Table *table, int row, int col, /* ==================================================== */ const char * -gnc_table_modify_update(Table *table, int row, int col, +gnc_table_modify_update(Table *table, + int row, int col, const char *oldval, const char *change, char *newval, @@ -1037,7 +1062,9 @@ gnc_table_modify_update(Table *table, int row, int col, const char * (*mv) (BasicCell *, const char *, const char *, const char *, int *, int *, int *); + const char *retval = NULL; + ENTER ("gnc_table_modify_update()\n"); /* OK, if there is a callback for this cell, call it */ @@ -1061,6 +1088,21 @@ gnc_table_modify_update(Table *table, int row, int col, retval = newval; (arr->cells[rel_row][rel_col])->changed = 0xffffffff; } + + if (table->set_help) + { + BasicCell *cell; + char *help_str; + + cell = arr->cells[rel_row][rel_col]; + help_str = xaccBasicCellGetHelp(cell); + + table->set_help(table, help_str, table->client_data); + + if (help_str != NULL) + free(help_str); + } + LEAVE ("gnc_table_modify_update(): " "change %d %d (relrow=%d relcol=%d) cell=%p val=%s\n", row, col, rel_row, rel_col, @@ -1071,6 +1113,54 @@ gnc_table_modify_update(Table *table, int row, int col, /* ==================================================== */ +gncBoolean +gnc_table_direct_update(Table *table, + int row, int col, + const char *oldval, + char **newval_ptr, + int *cursor_position, + int *start_selection, + int *end_selection, + void *gui_data) +{ + CellBlock *arr = table->current_cursor; + + const int rel_row = table->locators[row][col]->phys_row_offset; + const int rel_col = table->locators[row][col]->phys_col_offset; + + BasicCell *cell = arr->cells[rel_row][rel_col]; + + gncBoolean result; + + if (cell->direct_update == NULL) + return GNC_F; + + result = cell->direct_update(cell, oldval, newval_ptr, cursor_position, + start_selection, end_selection, gui_data); + + if ((*newval_ptr != oldval) && (*newval_ptr != NULL)) { + if (table->entries[row][col]) free (table->entries[row][col]); + table->entries[row][col] = *newval_ptr; + cell->changed = 0xffffffff; + } + + if (table->set_help) + { + char *help_str; + + help_str = xaccBasicCellGetHelp(cell); + + table->set_help(table, help_str, table->client_data); + + if (help_str != NULL) + free(help_str); + } + + return result; +} + +/* ==================================================== */ + gncBoolean gnc_table_find_valid_cell_horiz(Table *table, int *row, int *col, gncBoolean exact_cell) diff --git a/src/register/table-allgui.h b/src/register/table-allgui.h index 57817c60bb..eb7ebf34b9 100644 --- a/src/register/table-allgui.h +++ b/src/register/table-allgui.h @@ -154,6 +154,10 @@ struct _RevLocator { typedef struct _RevLocator RevLocator; +typedef void (*TableSetHelpFunc) (Table *table, + const char *help_str, + void *client_data); + /* The number of "physical" rows/cols is the number * of displayed one-line gui rows/cols in the table. @@ -202,6 +206,8 @@ struct _Table { int *p_new_phys_col, void *client_data); + TableSetHelpFunc set_help; + void * client_data; /* string values for each cell, @@ -214,6 +220,12 @@ struct _Table { uint32 **bg_colors; uint32 **fg_colors; + /* Determines whether the passive background + * colors alternate between odd and even virt + * rows, or between the first and non-first + * physical rows within cellblocks. */ + gncBoolean alternate_bg_colors; + /* handler locators for each cell, * of dimension num_phys_rows * num_phys_cols */ Locator ***locators; @@ -363,17 +375,20 @@ xaccRefreshCursorGUI (Table * table, gncBoolean do_scroll); * However, don't just change it, because it will break functional code. */ const char * -gnc_table_enter_update(Table *table, int row, int col, +gnc_table_enter_update(Table *table, + int row, int col, int *cursor_position, int *start_selection, int *end_selection); const char * -gnc_table_leave_update(Table *table, int row, int col, +gnc_table_leave_update(Table *table, + int row, int col, const char* old_text); const char * -gnc_table_modify_update(Table *table, int row, int col, +gnc_table_modify_update(Table *table, + int row, int col, const char *oldval, const char *change, char *newval, @@ -382,7 +397,18 @@ gnc_table_modify_update(Table *table, int row, int col, int *end_selection); gncBoolean -gnc_table_traverse_update(Table *table, int row, int col, +gnc_table_direct_update(Table *table, + int row, int col, + const char *oldval, + char **newval_ptr, + int *cursor_position, + int *start_selection, + int *end_selection, + void *gui_data); + +gncBoolean +gnc_table_traverse_update(Table *table, + int row, int col, gncTableTraversalDir dir, int *dest_row, int *dest_col); diff --git a/src/register/table-gnome.c b/src/register/table-gnome.c index a3210d354a..9c9e4566fd 100644 --- a/src/register/table-gnome.c +++ b/src/register/table-gnome.c @@ -60,7 +60,7 @@ xaccCreateTable (GtkWidget *widget, void *data) GnucashSheet *sheet; GnucashRegister *greg; Table *table; - + g_return_if_fail (widget != NULL); g_return_if_fail (GNUCASH_IS_REGISTER (widget)); g_return_if_fail (data != NULL); @@ -76,7 +76,7 @@ xaccCreateTable (GtkWidget *widget, void *data) gtk_widget_ref (table->table_widget); /* config the cell-block styles */ - + sheet->cursor_style[GNUCASH_CURSOR_HEADER] = gnucash_sheet_style_compile (sheet, sr->header, @@ -107,8 +107,6 @@ xaccCreateTable (GtkWidget *widget, void *data) gnucash_sheet_table_load (sheet); gnucash_sheet_cursor_set_from_table (sheet, TRUE); gnucash_sheet_redraw_all (sheet); - - return; } @@ -116,6 +114,8 @@ void xaccRefreshTableGUI (Table * table) { GnucashSheet *sheet; + SheetBlockStyle *style; + SplitRegister *sr; if (!table) return; @@ -123,8 +123,29 @@ xaccRefreshTableGUI (Table * table) return; g_return_if_fail (GNUCASH_IS_SHEET (table->table_widget)); - + sheet = GNUCASH_SHEET(table->table_widget); + sr = (SplitRegister *)sheet->split_register; + + style = sheet->cursor_style[GNUCASH_CURSOR_HEADER]; + gnucash_sheet_style_recompile (style, sr->header, sr, + GNUCASH_CURSOR_HEADER); + + style = sheet->cursor_style[GNUCASH_CURSOR_SINGLE]; + gnucash_sheet_style_recompile (style, sr->single_cursor, + sr, GNUCASH_CURSOR_SINGLE); + + style = sheet->cursor_style[GNUCASH_CURSOR_DOUBLE]; + gnucash_sheet_style_recompile (style, sr->double_cursor, + sr, GNUCASH_CURSOR_DOUBLE); + + style = sheet->cursor_style[GNUCASH_CURSOR_TRANS]; + gnucash_sheet_style_recompile (style, sr->trans_cursor, + sr, GNUCASH_CURSOR_TRANS); + + style = sheet->cursor_style[GNUCASH_CURSOR_SPLIT]; + gnucash_sheet_style_recompile (style, sr->split_cursor, + sr, GNUCASH_CURSOR_SPLIT); gnucash_sheet_table_load (sheet); diff --git a/src/scm/bootstrap.scm.in b/src/scm/bootstrap.scm.in index 2affc77308..e4d3acfdff 100644 --- a/src/scm/bootstrap.scm.in +++ b/src/scm/bootstrap.scm.in @@ -7,6 +7,20 @@ ;; (use-modules (gnc)) +(define (display-slib-error) + (display "Obtain slib at: http://swissnet.ai.mit.edu/~jaffer/SLIB.html\n") + (newline) + (display "If you have slib installed, you may need to create\n") + (display "a symbolic link named 'slib' from the guile directory\n") + (display "(usually /usr/share/guile) to the directory where slib\n") + (display "is installed.\n\n") + (display "You may also need to run the following command as root:") + (newline) + (newline) + (display " guile -c \"(use-modules (ice-9 slib)) (require 'new-catalog)\"") + (newline) + (newline)) + ;; Test for slib. (let* ((try-slib (lambda () (use-modules (ice-9 slib)))) (handler (lambda args #f)) @@ -14,27 +28,22 @@ (if (not result) (begin (newline) - (display "It appears you do not have the 'slib' scheme\n") - (display "library installed. You need slib2c6 or later\n") + (display "It appears you do not have the 'slib' scheme ") + (display "library installed.\nYou need slib2c4 or later ") (display "to run GnuCash.\n") (newline) - (display "Obtain slib at: http://swissnet.ai.mit.edu/~jaffer/SLIB.html\n") - (newline) + (display-slib-error) (exit 1)))) +;; This variable determines whether slib-backup.scm gets loaded. +(define gnc:*load-slib-backup* #f) + ;; Test for slib >= 2c6. (let* ((try-slib (lambda () (require 'printf) (sprintf #f "test"))) (handler (lambda args #f)) (result (catch #t try-slib handler))) (if (not result) - (begin - (newline) - (display "It appears your 'slib' scheme library is out\n") - (display "of date. You need slib2c6 or later to run GnuCash.\n") - (newline) - (display "Obtain slib at: http://swissnet.ai.mit.edu/~jaffer/SLIB.html\n") - (newline) - (exit 1)))) + (set! gnc:*load-slib-backup* #t))) (define (build-path firstelement . restofpath) diff --git a/src/scm/c-interface.scm b/src/scm/c-interface.scm index 2c5b1b7be5..1f2b7ce01f 100644 --- a/src/scm/c-interface.scm +++ b/src/scm/c-interface.scm @@ -1,4 +1,3 @@ -(use-modules (ice-9 slib)) (require 'hash-table) (define gnc:register-c-side-scheme-ptr #f) @@ -22,3 +21,23 @@ (set! gnc:register-c-side-scheme-ptr register-c-side-scheme-ptr) (set! gnc:unregister-c-side-scheme-ptr-id unregister-c-side-scheme-ptr-id)) + + +(define (gnc:error->string tag args) + (define (write-error port) + (if (and (list? args) (not (null? args))) + (let ((func (car args))) + (if func + (begin + (display "Function: " port) + (display func port) + (display ", " port) + (display tag port) + (display "\n\n" port))))) + (false-if-exception + (apply display-error #f port args)) + ;; Here we should write the stack trace. + ) + + (false-if-exception + (call-with-output-string write-error))) diff --git a/src/scm/extensions.scm b/src/scm/extensions.scm index c7579d1013..8689862121 100644 --- a/src/scm/extensions.scm +++ b/src/scm/extensions.scm @@ -1,19 +1,105 @@ (gnc:support "extensions.scm") + +(define (gnc:make-extension + ;; The type of extension item, either 'menu, 'menu-item, or 'separator + type + ;; The name of the extension in the menu + name + ;; The tooltip + documentation-string + ;; A list of names indicating the menus under which this item is + ;; located. The last item indicates the item *after* which this + ;; extension will go. + path + ;; The script to call when the menu item is selected + script) + (vector type + name + documentation-string + path + script)) + +(define (gnc:extension-type extension) + (vector-ref extension 0)) +(define (gnc:extension-name extension) + (vector-ref extension 1)) +(define (gnc:extension-documentation extension) + (vector-ref extension 2)) +(define (gnc:extension-path extension) + (vector-ref extension 3)) +(define (gnc:extension-script extension) + (vector-ref extension 4)) + +(define (gnc:make-menu-item name documentation-string path script) + (gnc:make-extension 'menu-item name documentation-string path script)) + +(define (gnc:make-menu name path) + (gnc:make-extension 'menu name #f path #f)) + +(define (gnc:make-separator path) + (gnc:make-extension 'separator #f #f path #f)) + + (define (gnc:extensions-menu-setup win) - ;; Should take window as a parameter? - - (gnc:debug "Setting up extensions menu " win "\n") - - (gnc:extensions-menu-add-item "Export data as text (Danger: Unfinished)" - "Export data as text." - (lambda () - (gnc:main-win-export-data-as-text win))) - (gnc:extensions-menu-add-item "QIF File Import (Danger: Unfinished)" - "Import QIF File - Scripted in Guile." - (lambda () - (gnc:extensions-qif-import win)))) + (define menu (gnc:make-menu "Extensions" (list "_Settings"))) -(gnc:hook-add-dangler gnc:*main-window-opened-hook* gnc:extensions-menu-setup) + (define export-item + (gnc:make-menu-item "Export data as text (Danger: Unfinished)" + "Export data as text." + (list "Extensions" "") + (lambda () (gnc:main-win-export-data-as-text win)))) + + (define qif-item + (gnc:make-menu-item "QIF File Import (Danger: Unfinished)" + "Import QIF File - Scripted in Guile." + (list "Extensions" + "Export data as text (Danger: Unfinished)") + (lambda () (gnc:extensions-qif-import win)))) + + (gnc:add-extension menu) + (gnc:add-extension export-item) + (gnc:add-extension qif-item)) + +;(gnc:hook-add-dangler gnc:*main-window-opened-hook* gnc:extensions-menu-setup) + +;; Automatically pick accelerators for menu names +(define (gnc:new-menu-namer) + + (define letter-hash (make-hash-table 23)) + (define name-hash (make-hash-table 23)) + + (define (add-name name) + + (define length (string-length name)) + + (define (try-at-k k) + (if (>= k length) + (begin + (hash-set! name-hash name name) + name) + (let* ((char (char-upcase (string-ref name k))) + (used (hash-ref letter-hash char))) + (if (not used) + (let ((new-name (string-append + (substring name 0 k) + "_" + (substring name k length)))) + (hash-set! letter-hash char #t) + (hash-set! name-hash name new-name) + new-name) + (try-at-k (+ k 1)))))) + + (try-at-k 0)) + + (define (lookup name) + (hash-ref name-hash name)) + + (define (dispatch key) + (case key + ((add-name) add-name) + ((lookup) lookup))) + + dispatch) diff --git a/src/scm/main.scm b/src/scm/main.scm index f04a008fe0..fc8dab45bc 100644 --- a/src/scm/main.scm +++ b/src/scm/main.scm @@ -1,4 +1,3 @@ - (define (gnc:startup) (gnc:debug "starting up.") (if (not (gnc:handle-command-line-args)) @@ -14,10 +13,9 @@ ;; Now we can load a bunch of files. (gnc:depend "doc.scm") - (gnc:depend "extensions.scm") ; Should this be here or somewhere else? + (gnc:depend "extensions.scm") (gnc:depend "text-export.scm") (gnc:depend "importqif.scm") - (gnc:depend "test.scm") (gnc:depend "report.scm") ;; FIXME: These do not belong here, but for now, we're putting them @@ -28,10 +26,18 @@ ;; ;; Just load these since we might want to redefine them on the fly ;; and we're going to change this mechanism anyway... - (gnc:load "report/hello-world.scm") - (gnc:load "report/balance-and-pnl.scm") - (gnc:load "report/transaction-report.scm") - (gnc:load "report/average-balance.scm") + (let + ((repdir + (opendir (string-append gnc:_share-dir-default_ "/scm/report")))) + (while (let ((cf (readdir repdir))) + (if (string? cf) + (if (and + (not (directory? cf)) + (> (string-length cf) 4)) + (if (string=? (substring cf (- (string-length cf) 4) + (string-length cf)) ".scm") + (gnc:load (string-append "report/" cf))))) + (string? cf)) ())) ;; Load the system configs (if (not (gnc:load-system-config-if-needed)) @@ -67,8 +73,8 @@ (gnc:ui-shutdown)))) (else - (gnc:hook-run-danglers gnc:*shutdown-hook*) (gnc:ui-destroy) + (gnc:hook-run-danglers gnc:*shutdown-hook*) (exit exit-status)))) (define (gnc:ui-finish) diff --git a/src/scm/options.scm b/src/scm/options.scm index beb27ec9cc..f7d8135f33 100644 --- a/src/scm/options.scm +++ b/src/scm/options.scm @@ -69,6 +69,10 @@ (let ((getter (gnc:option-getter option))) (getter))) +(define (gnc:option-default-value option) + (let ((getter (gnc:option-default-getter option))) + (getter))) + (define (gnc:restore-form-generator value->string) (lambda () (string-append "(lambda (option) " @@ -76,6 +80,10 @@ (value->string) ")))"))) +(define (gnc:value->string value) + (call-with-output-string + (lambda (port) (write value port)))) + (define (gnc:make-string-option section name @@ -83,7 +91,7 @@ documentation-string default-value) (let* ((value default-value) - (value->string (lambda () (string-append "\"" value "\"")))) + (value->string (lambda () (gnc:value->string value)))) (gnc:make-option section name sort-tag 'string documentation-string (lambda () value) @@ -102,7 +110,7 @@ documentation-string default-value) (let* ((value default-value) - (value->string (lambda () (if value "#t" "#f")))) + (value->string (lambda () (gnc:value->string value)))) (gnc:make-option section name sort-tag 'boolean documentation-string (lambda () value) @@ -129,10 +137,8 @@ (and (pair? date) (exact? (car date)) (exact? (cdr date)))) (let* ((value (default-getter)) - (value->string - (lambda () - (string-append "(" (number->string (car value)) - " . " (number->string (cdr value)) ")")))) + (value->string (lambda () + (string-append "'" (gnc:value->string value))))) (gnc:make-option section name sort-tag 'date documentation-string (lambda () value) @@ -200,7 +206,7 @@ (let* ((value default-value) (value->string (lambda () - (string-append "'" (symbol->string value))))) + (string-append "'" (gnc:value->string value))))) (gnc:make-option section name sort-tag 'multichoice documentation-string (lambda () value) @@ -216,6 +222,100 @@ (list #f "multichoice-option: illegal choice"))) ok-values))) +;; number range options use the option-data as a list whose +;; elements are: (lower-bound upper-bound num-decimals step-size) +(define (gnc:make-number-range-option + section + name + sort-tag + documentation-string + default-value + lower-bound + upper-bound + num-decimals + step-size) + (let* ((value default-value) + (value->string (lambda () (number->string value)))) + (gnc:make-option + section name sort-tag 'number-range documentation-string + (lambda () value) + (lambda (x) (set! value x)) + (lambda () default-value) + (gnc:restore-form-generator value->string) + (lambda (x) + (cond ((not (number? x)) (list #f "number-range-option: not a number")) + ((and (>= value lower-bound) + (<= value upper-bound)) + (list #t x)) + (else (list #f "number-range-option: out of range")))) + (list lower-bound upper-bound num-decimals step-size)))) + +;; Color options store rgba values in a list. +;; The option-data is a list, whose first element +;; is the range of possible rgba values and whose +;; second element is a boolean indicating whether +;; to use alpha transparency. +(define (gnc:make-color-option + section + name + sort-tag + documentation-string + default-value + range + use-alpha) + + (define (canonicalize values) + (map exact->inexact values)) + + (define (values-in-range values) + (if (null? values) + #t + (let ((value (car values))) + (and (number? value) + (>= value 0) + (<= value range) + (values-in-range (cdr values)))))) + + (define (validate-color color) + (cond ((not (list? color)) (list #f "color-option: not a list")) + ((not (= 4 (length color))) (list #f "color-option: wrong length")) + ((not (values-in-range color)) + (list #f "color-option: bad color values")) + (else (list #t color)))) + + (let* ((value (canonicalize default-value)) + (value->string (lambda () + (string-append "'" (gnc:value->string value))))) + (gnc:make-option + section name sort-tag 'color documentation-string + (lambda () value) + (lambda (x) (set! value (canonicalize x))) + (lambda () (canonicalize default-value)) + (gnc:restore-form-generator value->string) + validate-color + (list range use-alpha)))) + +(define (gnc:color->html color range) + + (define (html-value value) + (inexact->exact + (min 255.0 + (truncate (* (/ 255.0 range) value))))) + + (let ((red (car color)) + (green (cadr color)) + (blue (caddr color))) + (string-append + "#" + (number->string (html-value red) 16) + (number->string (html-value green) 16) + (number->string (html-value blue) 16)))) + +(define (gnc:color-option->html color-option) + (let ((color (gnc:option-value color-option)) + (range (car (gnc:option-data color-option)))) + (gnc:color->html color range))) + ;; Create a new options database (define (gnc:new-options) @@ -279,7 +379,8 @@ section-hash)) (hash-for-each (lambda (section hash) - (section-thunk section hash) + (if section-thunk + (section-thunk section hash)) (if option-thunk (section-for-each hash option-thunk))) option-hash)) @@ -302,16 +403,18 @@ (string-append "\n; Section: " section "\n\n") port)) (lambda (option) - (let* ((generator (gnc:option-generate-restore-form option)) - (restore-code (false-if-exception (generator)))) - (if restore-code - (display - (generate-option-restore-form option restore-code) - port)))))) + (let ((value (gnc:option-value option)) + (default-value (gnc:option-default-value option))) + (if + (not (equal? value default-value)) + (let* ((generator (gnc:option-generate-restore-form option)) + (restore-code (false-if-exception (generator)))) + (if restore-code + (display + (generate-option-restore-form option restore-code) + port)))))))) - (let ((header "; GnuCash Configuration Options\n\n") - (forms (call-with-output-string generate-forms))) - (string-append header forms))) + (call-with-output-string generate-forms)) (define (register-callback section name callback) (let ((id last-callback-id) @@ -344,16 +447,17 @@ (clear-changes)) (define (dispatch key) - (cond ((eq? key 'lookup) lookup-option) - ((eq? key 'register-option) register-option) - ((eq? key 'register-callback) register-callback) - ((eq? key 'unregister-callback-id) unregister-callback-id) - ((eq? key 'for-each) options-for-each) - ((eq? key 'for-each-general) options-for-each-general) - ((eq? key 'generate-restore-forms) generate-restore-forms) - ((eq? key 'clear-changes) clear-changes) - ((eq? key 'run-callbacks) run-callbacks) - (else (gnc:warn "options: bad key: " key "\n")))) + (case key + ((lookup) lookup-option) + ((register-option) register-option) + ((register-callback) register-callback) + ((unregister-callback-id) unregister-callback-id) + ((for-each) options-for-each) + ((for-each-general) options-for-each-general) + ((generate-restore-forms) generate-restore-forms) + ((clear-changes) clear-changes) + ((run-callbacks) run-callbacks) + (else (gnc:warn "options: bad key: " key "\n")))) dispatch) @@ -394,9 +498,10 @@ (gnc:option-db-register-option db_handle option)) options)) -(define (gnc:save-options options options-string file) +(define (gnc:save-options options options-string file header) (let ((code (gnc:generate-restore-forms options options-string)) (port (open file (logior O_WRONLY O_CREAT O_TRUNC)))) (if port (begin + (display header port) (display code port) (close port))))) diff --git a/src/scm/parseqif.scm b/src/scm/parseqif.scm index b29646361d..61a41688ea 100644 --- a/src/scm/parseqif.scm +++ b/src/scm/parseqif.scm @@ -1,7 +1,6 @@ ;;; $Id$ ;;;;;;;;;;; QIF Parsing ;;;;;;;;;;;;;; -(use-modules (ice-9 slib)) (require 'hash-table) (define qif-txn-list '()) @@ -213,4 +212,4 @@ (display "trans-jumptable") (display trans-jumptable) -(newline) \ No newline at end of file +(newline) diff --git a/src/scm/prefs.scm b/src/scm/prefs.scm index 4dd7a989ca..671ad24ac5 100644 --- a/src/scm/prefs.scm +++ b/src/scm/prefs.scm @@ -1,6 +1,5 @@ ;;;; Preferences... -(use-modules (ice-9 slib)) (require 'sort) (require 'hash-table) @@ -58,11 +57,18 @@ (gnc:make-home-dir) (gnc:save-options gnc:*options-entries* (symbol->string 'gnc:*options-entries*) - (build-path (getenv "HOME") ".gnucash" "config.auto"))) + (build-path (getenv "HOME") ".gnucash" "config.auto") + (string-append + "(gnc:config-file-format-version 1)\n\n" + "; GnuCash Configuration Options\n"))) + +(define (gnc:config-file-format-version version) #t) ;;;;;; Create default options and config vars +;; Account Types options + (gnc:register-configuration-option (gnc:make-simple-boolean-option "Account Types" "Show bank accounts" @@ -119,6 +125,8 @@ "k" "Show equity accounts in the account tree." #t)) +;; Account Fields options + (gnc:register-configuration-option (gnc:make-simple-boolean-option "Account Fields" "Show account name" @@ -159,6 +167,9 @@ "Account Fields" "Show account balance" "h" "Show the account balance column in the account tree." #t)) + +;; International options + (gnc:register-configuration-option (gnc:make-multichoice-option "International" "Date Format" @@ -166,8 +177,8 @@ (list #(us "US" "US-style: mm/dd/yyyy") #(uk "UK" "UK-style dd/mm/yyyy") #(ce "Europe" "Continental Europe: dd.mm.yyyy") - #(iso "ISO" "ISO Standard: yyyy-mm-dd") - #(locale "Locale" "Take from system locale")))) + #(iso "ISO" "ISO Standard: yyyy-mm-dd")))) +; #(locale "Locale" "Take from system locale")))) ;; hack alert - we should probably get the default new account currency ;; from the locale @@ -182,6 +193,9 @@ "International" "Use 24-hour time format" "c" "Use a 24 hour (instead of a 12 hour) time format." #f)) + +;; Register options + (gnc:register-configuration-option (gnc:make-multichoice-option "Register" "Default Register Mode" @@ -200,15 +214,150 @@ "Register" "Auto-Raise Lists" "b" "Automatically raise the list of accounts or actions during input." #t)) +(gnc:register-configuration-option + (gnc:make-simple-boolean-option + "Register" "Show All Transactions" + "c" "By default, show every transaction in an account." #t)) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "Register" "Number of Rows" + "d" "Default number of register rows to display." + 15.0 ;; default + 1.0 ;; lower bound + 200.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + + +;; Register Color options + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Header background" + "a" "The header background color" + (list #xff #xff #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Single mode default even row background" + "b" "The default background color for even rows in single mode" + (list #xcc #xcc #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Single mode default odd row background" + "bb" "The default background color for odd rows in single mode" + (list #xcc #xcc #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Single mode active background" + "c" "The background color for the active transaction in single mode" + (list #xff #xdd #xdd 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Double mode default even row background" + "d" "The default background color for even rows in double mode" + (list #xcc #xcc #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Double mode default odd row background" + "e" "The default background color for odd rows in double mode" + (list #xff #xff #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-simple-boolean-option + "Register Colors" "Double mode colors alternate with transactions" + "ee" "Alternate the even and odd colors with each transaction, not each row" + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Double mode active background" + "f" "The background color for the active transaction in double mode" + (list #xff #xdd #xdd 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Multi mode default transaction background" + "g" "The default background color for transactions in multi-line mode and the auto modes" + (list #xcc #xcc #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Multi mode active transaction background" + "h" "The background color for an active transaction in multi-line mode and the auto modes" + (list #xff #xdd #xdd 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Multi mode default split background" + "i" "The default background color for splits in multi-line mode and the auto modes" + (list #xff #xff #xff 0) + 255 + #f)) + +(gnc:register-configuration-option + (gnc:make-color-option + "Register Colors" "Multi mode active split background" + "j" "The background color for an active split in multi-line mode and the auto modes" + (list #xff #xff #xdd 0) + 255 + #f)) + + +;; General Options + +(gnc:register-configuration-option + (gnc:make-simple-boolean-option + "General" "Save Window Geometry" + "a" "Save window sizes and positions." #t)) + (gnc:register-configuration-option (gnc:make-multichoice-option "General" "Toolbar Buttons" - "a" "Choose whether to display icons, text, or both for toolbar buttons" + "b" "Choose whether to display icons, text, or both for toolbar buttons" 'icons_and_text (list #(icons_and_text "Icons and Text" "Show both icons and text") #(icons_only "Icons only" "Show icons only") #(text_only "Text only" "Show text only")))) +(gnc:register-configuration-option + (gnc:make-multichoice-option + "General" "Account Separator" + "c" "The character used to separate fully-qualified account names" + 'colon + (list #(colon ": (Colon)" "Income:Salary:Taxable") + #(slash "/ (Slash)" "Income/Salary/Taxable") + #(backslash "\\ (Backslash)" "Income\\Salary\\Taxable") + #(dash "- (Dash)" "Income-Salary-Taxable") + #(period ". (Period)" "Income.Salary.Taxable")))) + + +;; Configuation variables + (define gnc:*arg-show-version* (gnc:make-config-var "Show version." @@ -286,3 +435,87 @@ the current value of the path." #f))) equal? '(default))) + + +;; Internal options -- Section names that start with "__" are not +;; displayed in option dialogs. + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "account_add_win_width" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "account_add_win_height" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "account_edit_win_width" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "account_edit_win_height" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "main_win_width" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "main_win_height" #f #f + 400.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "reg_win_width" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) + +(gnc:register-configuration-option + (gnc:make-number-range-option + "__gui" "reg_stock_win_width" #f #f + 0.0 ;; default + 0.0 ;; lower bound + 32000.0 ;; upper bound + 0.0 ;; number of decimals + 1.0 ;; step size + )) diff --git a/src/scm/report.scm b/src/scm/report.scm index 04e2af3a8c..149051efa4 100644 --- a/src/scm/report.scm +++ b/src/scm/report.scm @@ -1,4 +1,3 @@ -(use-modules (ice-9 slib)) (require 'hash-table) (gnc:support "report.scm") @@ -46,17 +45,25 @@ (call-report rendering-thunk options))))) (define (gnc:report-menu-setup win) - ;; This should be on a reports menu later... + + (define menu (gnc:make-menu "_Reports" (list "_Settings"))) + (define menu-namer (gnc:new-menu-namer)) + + (gnc:add-extension menu) + (hash-for-each (lambda (name report) - (gnc:extensions-menu-add-item - (string-append "Report: " name) - (string-append "Display the " name " report.") - (lambda () - (let ((options (false-if-exception (gnc:report-new-options report)))) - (gnc:report-window (string-append "Report: " name) - (lambda () (gnc:run-report name options)) - options))))) + (define item + (gnc:make-menu-item + ((menu-namer 'add-name) name) + (string-append "Display the " name " report.") + (list "_Reports" "") + (lambda () + (let ((options (false-if-exception (gnc:report-new-options report)))) + (gnc:report-window (string-append "Report: " name) + (lambda () (gnc:run-report name options)) + options))))) + (gnc:add-extension item)) *gnc:_report-info_*)) (define (gnc:define-report version name option-generator rendering-thunk) diff --git a/src/scm/report/average-balance.scm b/src/scm/report/average-balance.scm index 986ef95f61..e895a241c9 100644 --- a/src/scm/report/average-balance.scm +++ b/src/scm/report/average-balance.scm @@ -1,43 +1,56 @@ ;; -*-scheme-*- ;; average-balance.scm ;; Report history of account balance and other info -;; Also graphs the information with gnuplot -;; Matt Martin +;; Plots the information with gnuplot +;; +;; Author makes no implicit or explicit guarantee of accuracy of +;; these calculations and accepts no responsibility for direct +;; or indirect losses incurred as a result of using this software. +;; +;; Note that this code uses functions defined in "transaction-report.scm" +;; Matt Martin -(use-modules (ice-9 slib)(ice-9 regex)) +(use-modules (ice-9 regex)) (require 'hash-table) -(require 'printf) -;hack alert - is this line necessary? -(gnc:depend "text-export.scm") +(gnc:depend "structure.scm") -(define datelist '()) - -;; Add delta to date, return result -(define (incdate adate delta) +;; Modify a date +(define (moddate op adate delta) (let ((newtm (localtime (car adate)))) (begin - (set-tm:mday newtm (+ (tm:mday newtm) (tm:mday delta))) - (set-tm:mon newtm (+ (tm:mon newtm) (tm:mon delta))) - (set-tm:year newtm (+ (tm:year newtm) (tm:year delta))) + (set-tm:sec newtm (op (tm:sec newtm) (tm:sec delta))) + (set-tm:min newtm (op (tm:min newtm) (tm:min delta))) + (set-tm:hour newtm (op (tm:hour newtm) (tm:hour delta))) + (set-tm:mday newtm (op (tm:mday newtm) (tm:mday delta))) + (set-tm:mon newtm (op (tm:mon newtm) (tm:mon delta))) + (set-tm:year newtm (op (tm:year newtm) (tm:year delta))) (let ((time (car (mktime newtm)))) (cons time 0)) )) ) -;; actual recursion for date list building +;; Add or subtract time from a date +(define (decdate adate delta)(moddate - adate delta )) +(define (incdate adate delta)(moddate + adate delta )) + +;; Time comparison, true if t2 is later than t1 +(define (gnc:timepair-later t1 t2) + (< (car t1) (car t2))) + +;; Build a list of time intervals (define (dateloop curd endd incr) - (cond ((gnc:timepair-later-date curd endd) - (cons curd (dateloop (incdate curd incr) endd incr))) - (else (list curd)) + (cond ((gnc:timepair-later curd endd) + (let ((nextd (incdate curd incr))) + (cons (list curd (decdate nextd SecDelta) '()) + (dateloop nextd endd incr)))) + (else '()) ) ) -;; Create list of dates to report on -(define (generate-datelist beg nd stp) - (set! datelist (dateloop beg nd (eval stp)))) +;; Options (define (runavg-options-generator) (define gnc:*runavg-track-options* (gnc:new-options)) @@ -78,12 +91,24 @@ "d" "Do transaction report on this account" (lambda () (let ((current-accounts (gnc:get-current-accounts)) - (num-accounts (gnc:group-get-num-accounts (gnc:get-current-group))) - (first-account (gnc:group-get-account (gnc:get-current-group) 0))) - (cond ((not (null? current-accounts)) (list (car current-accounts))) - ((> num-accounts 0) (list first-account)) - (else ())))) - #f #f)) + (num-accounts + (gnc:group-get-num-accounts (gnc:get-current-group)))) + + (cond ((not (null? current-accounts)) current-accounts) + (else + (let ((acctlist '())) + (gnc:for-loop + (lambda(x) + (set! acctlist + (append! + acctlist + (list (gnc:group-get-account + (gnc:get-current-group) x))))) + 0 (eval num-accounts) 1) + acctlist + ) + )))) + #f #t)) (gnc:register-runavg-option (gnc:make-multichoice-option @@ -96,16 +121,21 @@ #(YearDelta "Year" "Year") ))) + (gnc:register-runavg-option + (gnc:make-simple-boolean-option + "Report Options" "Sub-Accounts" + "e" "Add in sub-accounts of each selected" #f)) + (gnc:register-runavg-option (gnc:make-multichoice-option "Report Options" "Plot Type" - "b" "Get number at each one of these" 'NoPlot + "f" "Get number at each one of these" 'NoPlot (list #(NoPlot "Nothing" "Make No Plot") #(AvgBalPlot "Average" "Average Balance") #(GainPlot "Net Gain" "Net Gain") #(GLPlot "Gain/Loss" "Gain And Loss") ))) - + gnc:*runavg-track-options*) ; A reference zero date @@ -121,6 +151,9 @@ zd )) +(define SecDelta (let ((ddt (eval zdate))) + (set-tm:sec ddt 1) + ddt)) (define YearDelta (let ((ddt (eval zdate))) (set-tm:year ddt 1) ddt)) @@ -139,12 +172,13 @@ (set-tm:mon ddt 1) ddt)) -(define AvgBalPlot "using 1:2:3:4 t 'Average Balance' with errorbars") -(define GainPlot "using 1:5 t 'Net Gain' with linespoints") -(define GLPlot "using 1:7 t 'Losses' with lp, '' using 1:6 t 'Gains' with lp") +;; Plot strings +(define AvgBalPlot "using 2:3:4:5 t 'Average Balance' with errorbars, '' using 2:3 smooth sbezier t '' with lines") +(define GainPlot "using 2:6 t 'Net Gain' with linespoints, '' using 2:6 smooth sbezier t '' with lines" ) +(define GLPlot "using 2:8 t 'Losses' with lp, '' using 2:7 t 'Gains' with lp") (define NoPlot "") -;;; applies thunk to each split in account account +;; applies thunk to each split in account account (define (gnc:for-each-split-in-account account thunk) (gnc:for-loop (lambda (x) (thunk (gnc:account-get-split account x))) 0 (gnc:account-get-split-count account) 1)) @@ -163,30 +197,14 @@ (define (gnc:split-get-account-name split) (gnc:account-get-name (gnc:split-get-account split))) -;; timepair manipulation functions -;; hack alert - these should probably be put somewhere else -;; and be implemented PROPERLY rather than hackily - -(define (gnc:timepair-to-datestring tp) +(define (gnc:timepair-to-ldatestring tp) (let ((bdtime (localtime (car tp)))) (strftime "%m/%d/%Y" bdtime))) ;; Find difference in seconds (?) between time 1 and time2 (define (gnc:timepair-delta t1 t2) - (let ((time1 (car (gnc:timepair-canonical-day-time t1))) - (time2 (car (gnc:timepair-canonical-day-time t2)))) - (- time2 time1))) - -;; Don't know if these can be local+static to reduce-split-list -(define tempmax -1E10) -(define tempmin 1E10) -(define last 0) -(define zdate (cons 0 0)) -(define prevdate zdate) -(define avgaccum 0) -(define lossaccum 0) -(define gainaccum 0) + (- (car t2) (car t1))) ; Convert to string (define (tostring val) @@ -205,11 +223,13 @@ ; Create an html table row from a list of entries (define (html-table-row lst) - (string-append - (sprintf #f "") - (apply string-append (map html-table-col lst)) - (sprintf #f "\n") - ) + (cond ((string? lst) lst) + (else + (string-append + (sprintf #f "") + (apply string-append (map html-table-col lst)) + (sprintf #f "\n") + ))) ) ; Create an html table from a list of rows, each containing @@ -227,7 +247,7 @@ ) (define (html-table-header vec) - (apply string-append "" (map html-table-headcol vec)) + (apply string-append "
\n" (map html-table-headcol vec)) ) (define (html-table-footer) @@ -274,89 +294,199 @@ ) ) -;; Reset accumulators between time intervals -(define (resetvals bal curdate) - (set! tempmax bal) - (set! tempmin bal) - (set! prevdate curdate) - (set! avgaccum 0) - (set! lossaccum 0) - (set! gainaccum 0) -) +;; Returns sum of all vector elements after the first +(define (vector-sum v) + (let ((sum 0)) + (gnc:for-loop (lambda(i) (set! sum (+ sum (car (vector-ref v i))))) + 1 (vector-length v) 1) + sum)) -; -; Reduce a list of splits tl to a list of values at each date in datelist dl -; -(define (reduce-split-list dl tl pt) - (cond ((null? dl) '()) ;; End of recursion +; Datelist entry operators +(define (dl:begin dl) (car dl)) +(define (dl:end dl) (car (cdr dl))) + +(define (reduce-split-list dl tl pt av) + (let ((avgaccum 0) + (bals av) + (prevdate 0) + (balmin 10E9) + (balmax -10E9) + (gains 0) + (losses 0)) - (else (let* ((bal last) ;; get Balance and datelist "time" - (ct (car dl)) - (val 0)) + (define (procvals) + (let ((curbal (vector-sum (car (cdr (av 'x 0)))))) + (set! balmin (min balmin curbal)) + (set! balmax (max balmax curbal)))) - (begin - (cond ( (not (null? tl)) - ;; Get latest split values if any remain - (set! bal (gnc:split-get-balance (car tl))) - (set! val (gnc:split-get-value (car tl))) - (set! ct (gnc:split-get-transaction-date (car tl))) - )) + (define (accbal beg end) + (let ((curbal (vector-sum (car (cdr (av 'x 0)))))) + (set! avgaccum (+ avgaccum + (* curbal + (gnc:timepair-delta beg end))))) + ) - (cond ; past time interval bound ? - ((or(gnc:timepair-later-date (car dl) ct ) (null? tl)) - (cond ; Is this the first interval ? - ((= (car zdate) (car prevdate) ) - ; Start first date interval - (begin - (resetvals bal (car dl)) - (reduce-split-list (cdr dl) tl (car dl)) - ) + (define (calc-in-interval d tl) + (cond ((not (null? tl)) + + (let* ((bd (dl:begin d)) ; begin date + (ed (dl:end d)) ; end date + (cs (car tl)) ; current split + (cd (gnc:split-get-transaction-date cs)) ;current date + (an (gnc:split-get-account-name cs)) ; account name + (prevbal (vector-sum (car (cdr (av 'x 0)))))) + + (cond ((gnc:timepair-later cd bd) ;split before interval + (bals 'put an (gnc:split-get-balance cs)) + (calc-in-interval d (cdr tl)) ) - (else - (set! avgaccum ; sum up to now - (+ avgaccum - (* last (gnc:timepair-delta pt (car dl))))) - (cons ; form list of values for one "row" - (list - (gnc:timepair-to-datestring (car dl)) - (let ((dlta (gnc:timepair-delta prevdate (car dl)))) - (cond ((= dlta 0) 0); Should never happen ! - ((= avgaccum 0) bal) - (else (/ avgaccum dlta)))) - tempmax - tempmin - (- gainaccum lossaccum) - gainaccum - lossaccum - ) - (begin - (resetvals last (car dl)) - (reduce-split-list (cdr dl) tl (car dl)) - ) - ) - ))) - (else ; mid-interval - (begin - (set! tempmax (max tempmax bal)) - (set! tempmin (min tempmin bal)) - (set! avgaccum - (+ avgaccum - (* last (gnc:timepair-delta pt ct)))) - (cond ((> val 0) (set! gainaccum (+ gainaccum val))) - (else (set! lossaccum (- lossaccum val))) - ) - (set! last bal) - (reduce-split-list dl (cdr tl) ct) - )) - ) - ))))) + ((gnc:timepair-later cd ed) ;split is in the interval + (accbal prevdate cd) + (procvals) + (bals 'put an (gnc:split-get-balance cs)) + (let ((val (gnc:split-get-value cs))) + (cond ((< 0 val) (set! gains (+ gains val))) + (else (set! losses (- losses val))))) + + (procvals) ; catch all cases + (set! prevdate cd) + (calc-in-interval d (cdr tl)) + ) + + (else ; Past interval, nothing to do? + (accbal prevdate ed) + (procvals) + tl + ) + ))) + + (else ; Out of data ! + (accbal prevdate (dl:end d)) + (procvals) + tl + ) + ) + ) + + ;; Actual routine + (cond ((null? dl) '()) ;; End of recursion + (else + (let* ((bd (dl:begin (car dl))) + (ed (dl:end (car dl))) ) + + ;; Reset valaccumulator values + (set! prevdate bd) + (set! avgaccum 0) + (set! gains 0) + (set! losses 0) + + (let* ((rest (calc-in-interval (car dl) tl))) + ;; list of values for report + (cons + (list + (gnc:timepair-to-ldatestring bd) + (gnc:timepair-to-ldatestring ed) + (/ avgaccum + (gnc:timepair-delta bd ed)) + balmin balmax (- gains losses) gains losses) + + (reduce-split-list (cdr dl) rest pt av))) + ) + ) + ))) + +;; Pull a scheme list of splits from a C array +(define (gnc:convert-split-list slist) + (let ((numsplit 0) + (schsl '())) + (while + (let ((asplit (gnc:ith-split slist numsplit))) + (cond + ((pointer-token-null? asplit ) #f) + (else + (set! schsl (append! schsl (list asplit)) ) + (set! numsplit (+ numsplit 1)) + #t))) ()) + schsl + ) +) + +;; Pull a scheme list of accounts (including subaccounts) from group grp +(define (gnc:group-get-account-list grp) + (cond ((pointer-token-null? grp) '()) + (else + (let ((numacct 0) + (acctar (gnc:get-accounts grp)) + (schal '())) + + (while + (let ((anact (gnc:account-nth-account acctar numacct))) + (cond + ((pointer-token-null? anact ) #f) + (else + (set! schal (append! schal (list anact)) ) + (set! numacct (+ numacct 1)) + #t))) ()) + schal + ))) + ) + +(define (accumvects x y) + (cond + ((null? x) '()) + ((number? (car x)) + (cons (+ (car x) (car y)) (accumvects (cdr x) (cdr y)))) + (else (cons "x" (accumvects (cdr x) (cdr y))))) + ) + +;; Add x to list lst if it is not already in there +(define (addunique lst x) + (cond + ((null? lst) (list x)) ; all checked add it + (else (cond + ((equal? x (car lst)) lst) ; found, quit search and don't add again + (else (cons (car lst) (addunique (cdr lst) x))) ; keep searching + )))) + +;; Calculate averages of each column +(define (get-averages indata) + (let* ((avglst '())) + + (map (lambda (x) (set! avglst (append avglst (list 0.0)))) (car indata)) + + (map (lambda (x) + + (set! avglst (accumvects x avglst))) + indata) + (map (lambda (x) + (cond ((number? x) (/ x (length indata))) + (else ""))) + avglst) + )) + +;; Turn a C array of accounts into a scheme list of account names +(define (gnc:acctnames-from-list acctlist) + (let ((anlist '())) + (map (lambda(an) + (set! anlist (append! anlist + (list (gnc:account-get-name an))))) acctlist) + anlist)) (define acctcurrency "USD") (define acctname "") +(define (allsubaccounts accounts) + (cond ((null? accounts) '()) + (else +; (display (gnc:account-get-name (car accounts)))(newline) + (append + (gnc:group-get-account-list + (gnc:account-get-children (car accounts))) + (allsubaccounts (cdr accounts)))))) + (gnc:define-report ;; version 1 @@ -369,8 +499,10 @@ (let* ( (begindate (gnc:option-value (gnc:lookup-option options "Report Options" "From"))) - (enddate (gnc:lookup-option options "Report Options" "To")) - (stepsize (gnc:lookup-option options "Report Options" "Step Size")) + (enddate (gnc:option-value + (gnc:lookup-option options "Report Options" "To"))) + (stepsize (gnc:option-value + (gnc:lookup-option options "Report Options" "Step Size"))) (plotstr (gnc:option-value (gnc:lookup-option options "Report Options" "Plot Type"))) @@ -378,45 +510,94 @@ (accounts (gnc:option-value (gnc:lookup-option options "Report Options" "Account"))) + + (dosubs (gnc:option-value + (gnc:lookup-option options + "Report Options" "Sub-Accounts"))) + (prefix (list "" "")) (suffix (list "" "")) (collist - (list "Ending" "Average" "Max" "Min" "Net Gain" "Gain" "Loss")) + (list "Beginning" "Ending" "Average" "Max" "Min" "Net Gain" "Gain" "Loss")) (report-lines '()) (rept-data '()) + (sum-data '()) + (tempstruct '()) (rept-text "") + (gncq (gnc:malloc-query)) + + (slist '()) ) - - (generate-datelist - begindate - (gnc:option-value enddate) - (gnc:option-value stepsize)) + (gnc:init-query gncq) (if (null? accounts) (set! report-lines (list "")) (begin + ; Grab account names (set! acctname (gnc:account-get-name (car accounts))) - (set! acctcurrency (gnc:account-get-currency (car accounts))) - (gnc:for-each-split-in-account - (car accounts) - (lambda (split) - (set! report-lines - (append! report-lines (list split))))))) + (map (lambda(an) + (set! acctname + (string-append + acctname + " , " + (gnc:account-get-name an)))) + (cdr accounts) ) + + (cond ((equal? dosubs #t) + (map (lambda (a) + (set! accounts (addunique accounts a))) + (allsubaccounts accounts)) + (set! acctname (string-append acctname " and sub-accounts")) + )) + + (map (lambda(acct) (gnc:query-add-account gncq acct)) accounts) + + (set! tempstruct + (build-mystruct-instance + (define-mystruct + (gnc:acctnames-from-list accounts)))) + + (set! acctcurrency (gnc:account-get-currency (car accounts))) + + (set! report-lines + (gnc:convert-split-list (gnc:query-get-splits gncq))))) + + (gnc:free-query gncq) + (display (length report-lines)) (display " Splits\n") - (set! prevdate zdate) - (set! rept-data (reduce-split-list datelist report-lines (car datelist))) + + ; Set initial balances to zero + (map (lambda(an) (tempstruct 'put an 0)) + (gnc:acctnames-from-list accounts)) + + (dateloop begindate + enddate + (eval stepsize)) + (set! rept-data + (reduce-split-list + (dateloop begindate + enddate + (eval stepsize)) + report-lines zdate tempstruct)) + + (set! sum-data (get-averages rept-data)) + + + ;; Create HTML (set! rept-text (html-table collist - rept-data)) + (append rept-data + (list "" sum-data)))) + ;; Do a plot (if (not (equal? NoPlot (eval plotstr))) (let* ((fn "/tmp/gncplot.dat") (preplot (string-append @@ -435,6 +616,6 @@ (string-append "echo \"" preplot "plot '" fn "'" (eval plotstr) "\"|gnuplot -persist " ))) ) - - (append prefix (list rept-text) suffix))) + (append prefix (list "Report for " acctname "

\n" ) + (list rept-text) suffix))) ) diff --git a/src/scm/report/balance-and-pnl.scm b/src/scm/report/balance-and-pnl.scm index e335c52fd3..a0181e031a 100644 --- a/src/scm/report/balance-and-pnl.scm +++ b/src/scm/report/balance-and-pnl.scm @@ -1,8 +1,5 @@ ;; -*-scheme-*- -(use-modules (ice-9 slib)) -(require 'stdio) - (gnc:depend "text-export.scm") (let () diff --git a/src/scm/report/folio.scm b/src/scm/report/folio.scm index 5027aa36ea..e1d8650275 100644 --- a/src/scm/report/folio.scm +++ b/src/scm/report/folio.scm @@ -1,27 +1,27 @@ ;; I haven't finished converting this yet... -(gnc:define-report +;(gnc:define-report ;; version - 1 +; 1 ;; Menu name - "Folio" +; "Folio" ;; Options Generator - #f +; #f ;; Rendering thunk. See report.scm for details. - (lambda (options) - (list - "" - "" - "Portfolio Valuation" - "" +; (lambda (options) +; (list +; "" +; "" +; "Portfolio Valuation" +; "" - "" - "This page shows the valuation of your stock/mutual fund portfolio." - "
" - "You can create custom reports by editing the file" - "Reports/report-folio.phtml" - "

" +; "" +; "This page shows the valuation of your stock/mutual fund portfolio." +; "
" +; "You can create custom reports by editing the file" +; "Reports/report-folio.phtml" +; "

" ;; currency symbol that is printed is a dollar sign, for now ;; currency amounts get printed with two decimal places @@ -34,130 +34,125 @@ ;; This rouine accepts a pointer to a group, returns ;; a flat list of all of the children in the group. -sub account_flatlist -{ - local ($grp) = $_[0]; - local ($naccts) = gnucash::xaccGroupGetNumAccounts ($grp); - local ($n); - local (@acctlist, @childlist); - local ($children); +;sub account_flatlist +;{ +; local ($grp) = $_[0]; +; local ($naccts) = gnucash::xaccGroupGetNumAccounts ($grp); +; local ($n); +; local (@acctlist, @childlist); +; local ($children); - foreach $n (0..$naccts-1) { - $acct = gnucash::xaccGroupGetAccount ($grp, $n); - push (@acctlist, $acct); - $children = gnucash::xaccAccountGetChildren ($acct); - if ($children) { - @childlist = &account_flatlist ($children); - push (@acctlist, @childlist); - } - } +; foreach $n (0..$naccts-1) { +; $acct = gnucash::xaccGroupGetAccount ($grp, $n); +; push (@acctlist, $acct); +; $children = gnucash::xaccAccountGetChildren ($acct); +; if ($children) { +; @childlist = &account_flatlist ($children); +; push (@acctlist, @childlist); +; } +; } - return (@acctlist); -} +; return (@acctlist); +;} ;; -------------------------------------------------- ;; $split = &get_last_split ($account); ;; returns the most recent split in the account. -sub get_last_split -{ - local ($acct) = $_[0]; - local ($query, $splitlist, $split); +;sub get_last_split +;{ +; local ($acct) = $_[0]; +; local ($query, $splitlist, $split); - $query = gnucash::xaccMallocQuery(); - gnucash::xaccQueryAddAccount ($query, $acct); - gnucash::xaccQuerySetMaxSplits ($query, 1); - $splitlist = gnucash::xaccQueryGetSplits ($query); +; $query = gnucash::xaccMallocQuery(); +; gnucash::xaccQueryAddAccount ($query, $acct); +; gnucash::xaccQuerySetMaxSplits ($query, 1); +; $splitlist = gnucash::xaccQueryGetSplits ($query); - $split = gnucash::IthSplit ($splitlist, 0); -} +; $split = gnucash::IthSplit ($splitlist, 0); +;} ;; -------------------------------------------------- ;; get a flat list of all the accounts ... -@acclist = &account_flatlist ($topgroup); +;@acclist = &account_flatlist ($topgroup); ;; get the most recent price date .. -$latest = -1.0e20; -$earliest = 1.0e20; -foreach $acct (@acclist) -{ - $accntype = &gnucash::xaccAccountGetType($acct); - if (($accntype == $gnucash::STOCK) || - ($accntype == $gnucash::MUTUAL)) { - $split = &get_last_split ($acct); - $trans = gnucash::xaccSplitGetParent ($split); - $secs = gnucash::xaccTransGetDate ($trans); - if ($latest < $secs) { $latest = $secs; } - if ($earliest > $secs) { $earliest = $secs; } - } -} +;$latest = -1.0e20; +;$earliest = 1.0e20; +;foreach $acct (@acclist) +;{ +; $accntype = &gnucash::xaccAccountGetType($acct); +; if (($accntype == $gnucash::STOCK) || +; ($accntype == $gnucash::MUTUAL)) { +; $split = &get_last_split ($acct); +; $trans = gnucash::xaccSplitGetParent ($split); +; $secs = gnucash::xaccTransGetDate ($trans); +; if ($latest < $secs) { $latest = $secs; } +; if ($earliest > $secs) { $earliest = $secs; } +; } +;} -$ldayte = gnucash::xaccPrintDateSecs ($latest); -$edayte = gnucash::xaccPrintDateSecs ($earliest); +;$ldayte = gnucash::xaccPrintDateSecs ($latest); +;$edayte = gnucash::xaccPrintDateSecs ($earliest); -:> -

You have not selected an account.


- - -
Stock Portfolio Valuation -
Earliest Price <:= $edayte :>     Latest Price <:= $ldayte :> -
Name -Ticker -Shares -Recent Price -Value -Cost -Profit/Loss +; +; +; +;
Stock Portfolio Valuation +;
Earliest Price <:= $edayte :>     Latest Price <:= $ldayte :> +;
Name +;Ticker +;Shares +;Recent Price +;Value +;Cost +;Profit/Loss -<: +;$totvalue = 0; +;$totcost = 0; -$totvalue = 0; -$totcost = 0; +;foreach $acct (@acclist) +;{ -foreach $acct (@acclist) -{ +; $accntype = &gnucash::xaccAccountGetType($acct); +; if (($accntype == $gnucash::STOCK) || +; ($accntype == $gnucash::MUTUAL)) { - $accntype = &gnucash::xaccAccountGetType($acct); - if (($accntype == $gnucash::STOCK) || - ($accntype == $gnucash::MUTUAL)) { +; $accname = &gnucash::xaccAccountGetName($acct); +; $ticker = &gnucash::xaccAccountGetSecurity ($acct); +; $accbaln = &gnucash::xaccAccountGetBalance($acct); - $accname = &gnucash::xaccAccountGetName($acct); - $ticker = &gnucash::xaccAccountGetSecurity ($acct); - $accbaln = &gnucash::xaccAccountGetBalance($acct); +; $split = &get_last_split ($acct); +; $price = gnucash::xaccSplitGetSharePrice ($split); +; $shares = gnucash::xaccSplitGetShareBalance ($split); +; $value = gnucash::xaccSplitGetBalance ($split); +; $cost = gnucash::xaccSplitGetCostBasis ($split); +; $profit = $accbaln - $cost; - $split = &get_last_split ($acct); - $price = gnucash::xaccSplitGetSharePrice ($split); - $shares = gnucash::xaccSplitGetShareBalance ($split); - $value = gnucash::xaccSplitGetBalance ($split); - $cost = gnucash::xaccSplitGetCostBasis ($split); - $profit = $accbaln - $cost; +; $totvalue += $value; +; $totcost += $cost; - $totvalue += $value; - $totcost += $cost; +; print "
$accname"; +; print "$ticker"; +; printf "%10.3f", $shares; +; printf "\$%10.2f\n", $price; +; printf "\$%10.2f\n", $value; +; printf "\$%10.2f\n", $cost; +; printf "\$%10.2f\n", $profit; +; } +;} - print "
$accname"; - print "$ticker"; - printf "%10.3f", $shares; - printf "\$%10.2f\n", $price; - printf "\$%10.2f\n", $value; - printf "\$%10.2f\n", $cost; - printf "\$%10.2f\n", $profit; - } -} +;print "
   \n"; ;; blank line +;print "   \n"; ;; blank line -print "
   \n"; ;; blank line -print "   \n"; ;; blank line +;print "
Net "; +;print "  "; +;printf "  \$%10.2f \n", $totvalue; +;printf "  \$%10.2f \n", $totcost; +;printf "  \$%10.2f \n", $totvalue-$totcost; -print "
Net "; -print "  "; -printf "  \$%10.2f \n", $totvalue; -printf "  \$%10.2f \n", $totcost; -printf "  \$%10.2f \n", $totvalue-$totcost; - -:> - -
- - +;
+; +; diff --git a/src/scm/report/hello-world.scm b/src/scm/report/hello-world.scm index 376e410574..98cff8a252 100644 --- a/src/scm/report/hello-world.scm +++ b/src/scm/report/hello-world.scm @@ -21,7 +21,7 @@ (define (gnc:register-dummy-option new-option) (gnc:register-option gnc:*dummy-options* new-option)) - ;; This is a boolean option. It is in Section 'Page One' + ;; This is a boolean option. It is in Section 'Hello, World!' ;; and is named 'Boolean Option'. Its sorting key is 'a', ;; thus it will come before options with sorting keys ;; 'b', 'c', etc. in the same section. The default value @@ -30,7 +30,7 @@ ;; the mouse pointer over the option. (gnc:register-dummy-option (gnc:make-simple-boolean-option - "Page One" "Boolean Option" + "Hello, World!" "Boolean Option" "a" "This is a boolean option." #t)) ;; This is a multichoice option. The user can choose between @@ -40,8 +40,8 @@ ;; value is 'third. (gnc:register-dummy-option (gnc:make-multichoice-option - "Page Two" "Multi Choice Option" - "a" "This is a multi choice option." 'third + "Hello, World!" "Multi Choice Option" + "b" "This is a multi choice option." 'third (list #(first "First Option" "Help for first option.") #(second "Second Option" "Help for second option.") #(third "Third Option" "Help for third option.") @@ -54,8 +54,8 @@ ;; other key is 'a'. (gnc:register-dummy-option (gnc:make-string-option - "Page Two" "String Option" - "b" "This is a string option" "Hello, World")) + "Hello, World!" "String Option" + "c" "This is a string option" "Hello, World")) ;; This is a date/time option. The user can pick a date and, ;; possibly, a time. Times are stored as a pair @@ -65,8 +65,8 @@ ;; time. (gnc:register-dummy-option (gnc:make-date-option - "Time and Date" "Just a Date Option" - "a" "This is a date option" + "Hello, World!" "Just a Date Option" + "d" "This is a date option" (lambda () (cons (current-time) 0)) #f)) @@ -74,11 +74,26 @@ ;; the time. (gnc:register-dummy-option (gnc:make-date-option - "Time and Date" "Time and Date Option" - "b" "This is a date option with time" + "Hello, World!" "Time and Date Option" + "e" "This is a date option with time" (lambda () (cons (current-time) 0)) #t)) + ;; This is a color option, defined by rgba values. A color value + ;; is a list where the elements are the red, green, blue, and + ;; alpha channel values respectively. The penultimate argument + ;; (255) is the allowed range of rgba values. The final argument + ;; (#f) indicates the alpha value should be ignored. You can get + ;; a color string from a color option with gnc:color-option->html, + ;; which will scale the values appropriately according the range. + (gnc:register-dummy-option + (gnc:make-color-option + "Hello, World!" "Background Color" + "f" "This is a color option" + (list #x99 #xcc #xff 0) + 255 + #f)) + ;; This is an account list option. The user can select one ;; or (possibly) more accounts from the list of accounts ;; in the current file. Values are scheme handles to actual @@ -96,11 +111,20 @@ ;; selected account in the main window, if any. (gnc:register-dummy-option (gnc:make-account-list-option - "Page One" "An account list option" - "b" "This is an account list option" + "Hello, World!" "An account list option" + "g" "This is an account list option" (lambda () (gnc:get-current-accounts)) #f #t)) + ;; This option is for testing. When true, the report generates + ;; an exception. + (gnc:register-dummy-option + (gnc:make-simple-boolean-option + "Testing" "Crash the report" + "a" (string-append "This is for testing. " + "Your reports probably shouldn't have an " + "option like this.") #f)) + gnc:*dummy-options*) ;; This is a helper function to generate an html list of account names @@ -138,33 +162,43 @@ ;; options in the set of options given to the function. This set will ;; be generated by the options generator above. ;; Use (gnc:lookup-option options section name) to get the option. - (let ((boolop (gnc:lookup-option options "Page One" "Boolean Option")) + (let ((boolop (gnc:lookup-option options + "Hello, World!" "Boolean Option")) (multop (gnc:lookup-option options - "Page Two" "Multi Choice Option")) - (strop (gnc:lookup-option options "Page Two" "String Option")) + "Hello, World!" "Multi Choice Option")) + (strop (gnc:lookup-option options + "Hello, World!" "String Option")) (dateop (gnc:lookup-option options - "Time and Date" "Just a Date Option")) + "Hello, World!" "Just a Date Option")) (dateop2 (gnc:lookup-option options - "Time and Date" "Time and Date Option")) + "Hello, World!" "Time and Date Option")) + (colorop (gnc:lookup-option options + "Hello, World!" "Background Color")) (account-list-op (gnc:lookup-option options - "Page One" - "An account list option"))) - (let ((time-string (strftime "%c" (localtime (current-time)))) + "Hello, World!" + "An account list option")) + (crash-op (gnc:lookup-option options "Testing" "Crash the report"))) + + (let ((time-string (strftime "%X" (localtime (current-time)))) (date-string (strftime "%x" (localtime (car (gnc:option-value dateop))))) - (date-string2 (strftime "%c" + (date-string2 (strftime "%x %X" (localtime (car (gnc:option-value dateop2)))))) + ;; Crash if asked to. + (if (gnc:option-value crash-op) + (string-length #f)) ;; this will crash + ;; Here's where we generate the html. A real report would need ;; much more code and involve many more utility functions. See ;; the other reports for details. Note that you can used nested ;; lists here, as well as arbitrary functions. (list "" - "" + "" - "

Hello, World Report

" + "

Hello, World

" (list "

" "This is a sample GnuCash report. " diff --git a/src/scm/report/transaction-report.scm b/src/scm/report/transaction-report.scm index 6ef5accea6..dba8a1ab6a 100644 --- a/src/scm/report/transaction-report.scm +++ b/src/scm/report/transaction-report.scm @@ -3,8 +3,6 @@ ;; Report on all transactions in an account ;; Robert Merkel (rgmerk@mira.net) -(use-modules (ice-9 slib)) -(require 'printf) (require 'sort) ;hack alert - is this line necessary? @@ -215,7 +213,7 @@ (define (gnc:tr-report-get-first-acc-name split-scm) (let ((other-splits (gnc:tr-report-get-other-splits split-scm))) - (cond ((= (length other-splits) 0) "") + (cond ((= (length other-splits) 0) "-") (else (caar other-splits))))) ;;; something like diff --git a/src/scm/slib-backup.scm b/src/scm/slib-backup.scm new file mode 100644 index 0000000000..48defbaaad --- /dev/null +++ b/src/scm/slib-backup.scm @@ -0,0 +1,1212 @@ +;; This is a portion of slib2c7 which has been included +;; with GnuCash in case the user has slib2c4, which is +;; lacking some functionality needed by GnuCash. This +;; file is only loaded if there is no later version of +;; slib available. + +;; The complete distribution of slib is available at +;; http://swissnet.ai.mit.edu/~jaffer/SLIB.html + +;; Once slib2c6 or better becomes commonly available, +;; this file should be removed. + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;;;"alist.scm", alist functions for Scheme. +;;;Copyright (c) 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (predicate->asso pred) + (cond ((eq? eq? pred) assq) + ((eq? = pred) assv) + ((eq? eqv? pred) assv) + ((eq? char=? pred) assv) + ((eq? equal? pred) assoc) + ((eq? string=? pred) assoc) + (else (lambda (key alist) + (let l ((al alist)) + (cond ((null? al) #f) + ((pred key (caar al)) (car al)) + (else (l (cdr al))))))))) + +(define (alist-inquirer pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key) + (let ((pair (assofun key alist))) + (and pair (cdr pair)))))) + +(define (alist-associator pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key val) + (let* ((pair (assofun key alist))) + (cond (pair (set-cdr! pair val) + alist) + (else (cons (cons key val) alist))))))) + +(define (alist-remover pred) + (lambda (alist key) + (cond ((null? alist) alist) + ((pred key (caar alist)) (cdr alist)) + ((null? (cdr alist)) alist) + ((pred key (caadr alist)) + (set-cdr! alist (cddr alist)) alist) + (else + (let l ((al (cdr alist))) + (cond ((null? (cdr al)) alist) + ((pred key (caadr al)) + (set-cdr! al (cddr al)) alist) + (else (l (cdr al))))))))) + +(define (alist-map proc alist) + (map (lambda (pair) (cons (car pair) (proc (car pair) (cdr pair)))) + alist)) + +(define (alist-for-each proc alist) + (for-each (lambda (pair) (proc (car pair) (cdr pair))) alist)) + + +; "hash.scm", hashing functions for Scheme. +; Copyright (c) 1992, 1993, 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (hash:hash-char-ci char n) + (modulo (char->integer (char-downcase char)) n)) + +(define hash:hash-char hash:hash-char-ci) + +(define (hash:hash-symbol sym n) + (hash:hash-string (symbol->string sym) n)) + +;;; This can overflow on implemenatations where inexacts have a larger +;;; range than exact integers. +(define hash:hash-number + (if (provided? 'inexact) + (lambda (num n) + (if (integer? num) + (modulo (if (exact? num) num (inexact->exact num)) n) + (hash:hash-string-ci + (number->string (if (exact? num) (exact->inexact num) num)) + n))) + (lambda (num n) + (if (integer? num) + (modulo num n) + (hash:hash-string-ci (number->string num) n))))) + +(define (hash:hash-string-ci str n) + (let ((len (string-length str))) + (if (> len 5) + (let loop ((h (modulo 264 n)) (i 5)) + (if (positive? i) + (loop (modulo (+ (* h 256) + (char->integer + (char-downcase + (string-ref str (modulo h len))))) + n) + (- i 1)) + h)) + (let loop ((h 0) (i (- len 1))) + (if (>= i 0) + (loop (modulo (+ (* h 256) + (char->integer + (char-downcase (string-ref str i)))) + n) + (- i 1)) + h))))) + +(define hash:hash-string hash:hash-string-ci) + +(define (hash:hash obj n) + (let hs ((d 10) (obj obj)) + (cond + ((number? obj) (hash:hash-number obj n)) + ((char? obj) (modulo (char->integer (char-downcase obj)) n)) + ((symbol? obj) (hash:hash-symbol obj n)) + ((string? obj) (hash:hash-string obj n)) + ((vector? obj) + (let ((len (vector-length obj))) + (if (> len 5) + (let lp ((h 1) (i (quotient d 2))) + (if (positive? i) + (lp (modulo (+ (* h 256) + (hs 2 (vector-ref obj (modulo h len)))) + n) + (- i 1)) + h)) + (let loop ((h (- n 1)) (i (- len 1))) + (if (>= i 0) + (loop (modulo (+ (* h 256) (hs (quotient d len) + (vector-ref obj i))) + n) + (- i 1)) + h))))) + ((pair? obj) + (if (positive? d) (modulo (+ (hs (quotient d 2) (car obj)) + (hs (quotient d 2) (cdr obj))) + n) + 1)) + (else + (modulo + (cond + ((null? obj) 256) + ((boolean? obj) (if obj 257 258)) + ((eof-object? obj) 259) + ((input-port? obj) 260) + ((output-port? obj) 261) + ((procedure? obj) 262) + ((and (provided? 'RECORD) (record? obj)) + (let* ((rtd (record-type-descriptor obj)) + (fns (record-type-field-names rtd)) + (len (length fns))) + (if (> len 5) + (let lp ((h (modulo 266 n)) (i (quotient d 2))) + (if (positive? i) + (lp (modulo + (+ (* h 256) + (hs 2 ((record-accessor + rtd (list-ref fns (modulo h len))) + obj))) + n) + (- i 1)) + h)) + (let loop ((h (- n 1)) (i (- len 1))) + (if (>= i 0) + (loop (modulo + (+ (* h 256) + (hs (quotient d len) + ((record-accessor + rtd (list-ref fns (modulo h len))) + obj))) + n) + (- i 1)) + h))))) + (else 263)) + n))))) + +(define hash hash:hash) +(define hashv hash:hash) + +;;; Object-hash is somewhat expensive on copying GC systems (like +;;; PC-Scheme and MITScheme). We use it only on strings, pairs, +;;; vectors, and records. This also allows us to use it for both +;;; hashq and hashv. + +(if (provided? 'object-hash) + (set! hashv + (if (provided? 'record) + (lambda (obj k) + (if (or (string? obj) (pair? obj) (vector? obj) (record? obj)) + (modulo (object-hash obj) k) + (hash:hash obj k))) + (lambda (obj k) + (if (or (string? obj) (pair? obj) (vector? obj)) + (modulo (object-hash obj) k) + (hash:hash obj k)))))) + +(define hashq hashv) + + +; "hashtab.scm", hash tables for Scheme. +; Copyright (c) 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (predicate->hash pred) + (cond ((eq? pred eq?) hashq) + ((eq? pred eqv?) hashv) + ((eq? pred equal?) hash) + ((eq? pred =) hashv) + ((eq? pred char=?) hashv) + ((eq? pred char-ci=?) hashv) + ((eq? pred string=?) hash) + ((eq? pred string-ci=?) hash) + (else (slib:error "unknown predicate for hash" pred)))) + +(define (make-hash-table k) (make-vector k '())) + +(define (predicate->hash-asso pred) + (let ((hashfun (predicate->hash pred)) + (asso (predicate->asso pred))) + (lambda (key hashtab) + (asso key + (vector-ref hashtab (hashfun key (vector-length hashtab))))))) + +(define (hash-inquirer pred) + (let ((hashfun (predicate->hash pred)) + (ainq (alist-inquirer pred))) + (lambda (hashtab key) + (ainq (vector-ref hashtab (hashfun key (vector-length hashtab))) + key)))) + +(define (hash-associator pred) + (let ((hashfun (predicate->hash pred)) + (asso (alist-associator pred))) + (lambda (hashtab key val) + (let* ((num (hashfun key (vector-length hashtab)))) + (vector-set! hashtab num + (asso (vector-ref hashtab num) key val))) + hashtab))) + +(define (hash-remover pred) + (let ((hashfun (predicate->hash pred)) + (arem (alist-remover pred))) + (lambda (hashtab key) + (let* ((num (hashfun key (vector-length hashtab)))) + (vector-set! hashtab num + (arem (vector-ref hashtab num) key))) + hashtab))) + +(define (hash-map proc ht) + (define nht (make-vector (vector-length ht))) + (do ((i (+ -1 (vector-length ht)) (+ -1 i))) + ((negative? i) nht) + (vector-set! + nht i + (alist-map proc (vector-ref ht i))))) + +(define (hash-for-each proc ht) + (do ((i (+ -1 (vector-length ht)) (+ -1 i))) + ((negative? i)) + (alist-for-each proc (vector-ref ht i)))) + + +;;; "strcase.scm" String casing functions. +; Written 1992 by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de) +; +; This code is in the public domain. + +; Modified by Aubrey Jaffer Nov 1992. +; Authors of the original version were Ken Dickey and Aubrey Jaffer. + +;string-upcase, string-downcase, string-capitalize +; are obvious string conversion procedures and are non destructive. +;string-upcase!, string-downcase!, string-capitalize! +; are destructive versions. + +(define (string-upcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-upcase (string-ref str i))))) + +(define (string-upcase str) + (string-upcase! (string-copy str))) + +(define (string-downcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-downcase (string-ref str i))))) + +(define (string-downcase str) + (string-downcase! (string-copy str))) + +(define (string-capitalize! str) ; "hello" -> "Hello" + (let ((non-first-alpha #f) ; "hELLO" -> "Hello" + (str-len (string-length str))) ; "*hello" -> "*Hello" + (do ((i 0 (+ i 1))) ; "hello you" -> "Hello You" + ((= i str-len) str) + (let ((c (string-ref str i))) + (if (char-alphabetic? c) + (if non-first-alpha + (string-set! str i (char-downcase c)) + (begin + (set! non-first-alpha #t) + (string-set! str i (char-upcase c)))) + (set! non-first-alpha #f)))))) + +(define (string-capitalize str) + (string-capitalize! (string-copy str))) + +(define string-ci->symbol + (if (equal? "a" (symbol->string 'a)) + (lambda (str) (string->symbol (string-downcase str))) + (lambda (str) (string->symbol (string-upcase str))))) + + +;;"genwrite.scm" generic write used by pretty-print and truncated-print. +;; Copyright (c) 1991, Marc Feeley +;; Author: Marc Feeley (feeley@iro.umontreal.ca) +;; Distribution restrictions: none + +(define (generic-write obj display? width output) + + (define (read-macro? l) + (define (length1? l) (and (pair? l) (null? (cdr l)))) + (let ((head (car l)) (tail (cdr l))) + (case head + ((QUOTE QUASIQUOTE UNQUOTE UNQUOTE-SPLICING) (length1? tail)) + (else #f)))) + + (define (read-macro-body l) + (cadr l)) + + (define (read-macro-prefix l) + (let ((head (car l)) (tail (cdr l))) + (case head + ((QUOTE) "'") + ((QUASIQUOTE) "`") + ((UNQUOTE) ",") + ((UNQUOTE-SPLICING) ",@")))) + + (define (out str col) + (and col (output str) (+ col (string-length str)))) + + (define (wr obj col) + + (define (wr-expr expr col) + (if (read-macro? expr) + (wr (read-macro-body expr) (out (read-macro-prefix expr) col)) + (wr-lst expr col))) + + (define (wr-lst l col) + (if (pair? l) + (let loop ((l (cdr l)) + (col (and col (wr (car l) (out "(" col))))) + (cond ((not col) col) + ((pair? l) + (loop (cdr l) (wr (car l) (out " " col)))) + ((null? l) (out ")" col)) + (else (out ")" (wr l (out " . " col)))))) + (out "()" col))) + + (cond ((pair? obj) (wr-expr obj col)) + ((null? obj) (wr-lst obj col)) + ((vector? obj) (wr-lst (vector->list obj) (out "#" col))) + ((boolean? obj) (out (if obj "#t" "#f") col)) + ((number? obj) (out (number->string obj) col)) + ((symbol? obj) (out (symbol->string obj) col)) + ((procedure? obj) (out "#[procedure]" col)) + ((string? obj) (if display? + (out obj col) + (let loop ((i 0) (j 0) (col (out "\"" col))) + (if (and col (< j (string-length obj))) + (let ((c (string-ref obj j))) + (if (or (char=? c #\\) + (char=? c #\")) + (loop j + (+ j 1) + (out "\\" + (out (substring obj i j) + col))) + (loop i (+ j 1) col))) + (out "\"" + (out (substring obj i j) col)))))) + ((char? obj) (if display? + (out (make-string 1 obj) col) + (out (case obj + ((#\space) "space") + ((#\newline) "newline") + (else (make-string 1 obj))) + (out "#\\" col)))) + ((input-port? obj) (out "#[input-port]" col)) + ((output-port? obj) (out "#[output-port]" col)) + ((eof-object? obj) (out "#[eof-object]" col)) + (else (out "#[unknown]" col)))) + + (define (pp obj col) + + (define (spaces n col) + (if (> n 0) + (if (> n 7) + (spaces (- n 8) (out " " col)) + (out (substring " " 0 n) col)) + col)) + + (define (indent to col) + (and col + (if (< to col) + (and (out (make-string 1 #\newline) col) (spaces to 0)) + (spaces (- to col) col)))) + + (define (pr obj col extra pp-pair) + (if (or (pair? obj) (vector? obj)) ; may have to split on multiple lines + (let ((result '()) + (left (min (+ (- (- width col) extra) 1) max-expr-width))) + (generic-write obj display? #f + (lambda (str) + (set! result (cons str result)) + (set! left (- left (string-length str))) + (> left 0))) + (if (> left 0) ; all can be printed on one line + (out (reverse-string-append result) col) + (if (pair? obj) + (pp-pair obj col extra) + (pp-list (vector->list obj) (out "#" col) extra pp-expr)))) + (wr obj col))) + + (define (pp-expr expr col extra) + (if (read-macro? expr) + (pr (read-macro-body expr) + (out (read-macro-prefix expr) col) + extra + pp-expr) + (let ((head (car expr))) + (if (symbol? head) + (let ((proc (style head))) + (if proc + (proc expr col extra) + (if (> (string-length (symbol->string head)) + max-call-head-width) + (pp-general expr col extra #f #f #f pp-expr) + (pp-call expr col extra pp-expr)))) + (pp-list expr col extra pp-expr))))) + + ; (head item1 + ; item2 + ; item3) + (define (pp-call expr col extra pp-item) + (let ((col* (wr (car expr) (out "(" col)))) + (and col + (pp-down (cdr expr) col* (+ col* 1) extra pp-item)))) + + ; (item1 + ; item2 + ; item3) + (define (pp-list l col extra pp-item) + (let ((col (out "(" col))) + (pp-down l col col extra pp-item))) + + (define (pp-down l col1 col2 extra pp-item) + (let loop ((l l) (col col1)) + (and col + (cond ((pair? l) + (let ((rest (cdr l))) + (let ((extra (if (null? rest) (+ extra 1) 0))) + (loop rest + (pr (car l) (indent col2 col) extra pp-item))))) + ((null? l) + (out ")" col)) + (else + (out ")" + (pr l + (indent col2 (out "." (indent col2 col))) + (+ extra 1) + pp-item))))))) + + (define (pp-general expr col extra named? pp-1 pp-2 pp-3) + + (define (tail1 rest col1 col2 col3) + (if (and pp-1 (pair? rest)) + (let* ((val1 (car rest)) + (rest (cdr rest)) + (extra (if (null? rest) (+ extra 1) 0))) + (tail2 rest col1 (pr val1 (indent col3 col2) extra pp-1) col3)) + (tail2 rest col1 col2 col3))) + + (define (tail2 rest col1 col2 col3) + (if (and pp-2 (pair? rest)) + (let* ((val1 (car rest)) + (rest (cdr rest)) + (extra (if (null? rest) (+ extra 1) 0))) + (tail3 rest col1 (pr val1 (indent col3 col2) extra pp-2))) + (tail3 rest col1 col2))) + + (define (tail3 rest col1 col2) + (pp-down rest col2 col1 extra pp-3)) + + (let* ((head (car expr)) + (rest (cdr expr)) + (col* (wr head (out "(" col)))) + (if (and named? (pair? rest)) + (let* ((name (car rest)) + (rest (cdr rest)) + (col** (wr name (out " " col*)))) + (tail1 rest (+ col indent-general) col** (+ col** 1))) + (tail1 rest (+ col indent-general) col* (+ col* 1))))) + + (define (pp-expr-list l col extra) + (pp-list l col extra pp-expr)) + + (define (pp-LAMBDA expr col extra) + (pp-general expr col extra #f pp-expr-list #f pp-expr)) + + (define (pp-IF expr col extra) + (pp-general expr col extra #f pp-expr #f pp-expr)) + + (define (pp-COND expr col extra) + (pp-call expr col extra pp-expr-list)) + + (define (pp-CASE expr col extra) + (pp-general expr col extra #f pp-expr #f pp-expr-list)) + + (define (pp-AND expr col extra) + (pp-call expr col extra pp-expr)) + + (define (pp-LET expr col extra) + (let* ((rest (cdr expr)) + (named? (and (pair? rest) (symbol? (car rest))))) + (pp-general expr col extra named? pp-expr-list #f pp-expr))) + + (define (pp-BEGIN expr col extra) + (pp-general expr col extra #f #f #f pp-expr)) + + (define (pp-DO expr col extra) + (pp-general expr col extra #f pp-expr-list pp-expr-list pp-expr)) + + ; define formatting style (change these to suit your style) + + (define indent-general 2) + + (define max-call-head-width 5) + + (define max-expr-width 50) + + (define (style head) + (case head + ((LAMBDA LET* LETREC DEFINE) pp-LAMBDA) + ((IF SET!) pp-IF) + ((COND) pp-COND) + ((CASE) pp-CASE) + ((AND OR) pp-AND) + ((LET) pp-LET) + ((BEGIN) pp-BEGIN) + ((DO) pp-DO) + (else #f))) + + (pr obj col 0 pp-expr)) + + (if width + (out (make-string 1 #\newline) (pp obj 0)) + (wr obj 0))) + +; (reverse-string-append l) = (apply string-append (reverse l)) + +(define (reverse-string-append l) + + (define (rev-string-append l i) + (if (pair? l) + (let* ((str (car l)) + (len (string-length str)) + (result (rev-string-append (cdr l) (+ i len)))) + (let loop ((j 0) (k (- (- (string-length result) i) len))) + (if (< j len) + (begin + (string-set! result k (string-ref str j)) + (loop (+ j 1) (+ k 1))) + result))) + (make-string i))) + + (rev-string-append l 0)) + + +;;;; "printf.scm" Implementation of standard C functions for Scheme +;;; Copyright (C) 1991-1993, 1996 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;; Parse the output of NUMBER->STRING. +;; Returns a list: (sign-character digit-string exponent-integer) +;; SIGN-CHAR will be either #\+ or #\-, DIGIT-STRING will always begin +;; with a "0", after which a decimal point should be understood. +;; If STR denotes a non-real number, 3 additional elements for the +;; complex part are appended. +(define (stdio:parse-float str) + (let ((n (string-length str)) + (iend 0)) + (letrec ((prefix + (lambda (i rest) + (if (and (< i (- n 1)) + (char=? #\# (string-ref str i))) + (case (string-ref str (+ i 1)) + ((#\d #\i #\e) (prefix (+ i 2) rest)) + ((#\.) (rest i)) + (else (parse-error))) + (rest i)))) + (sign + (lambda (i rest) + (if (< i n) + (let ((c (string-ref str i))) + (case c + ((#\- #\+) (cons c (rest (+ i 1)))) + (else (cons #\+ (rest i)))))))) + (digits + (lambda (i rest) + (do ((j i (+ j 1))) + ((or (>= j n) + (not (or (char-numeric? (string-ref str j)) + (char=? #\# (string-ref str j))))) + (cons + (if (= i j) "0" (substring str i j)) + (rest j)))))) + (point + (lambda (i rest) + (if (and (< i n) + (char=? #\. (string-ref str i))) + (rest (+ i 1)) + (rest i)))) + (exp + (lambda (i) + (if (< i n) + (case (string-ref str i) + ((#\e #\s #\f #\d #\l #\E #\S #\F #\D #\L) + (let ((s (sign (+ i 1) (lambda (i) (digits i end!))))) + (list + (if (char=? #\- (car s)) + (- (string->number (cadr s))) + (string->number (cadr s)))))) + (else (end! i) + '(0))) + (begin (end! i) + '(0))))) + (end! + (lambda (i) + (set! iend i) + '())) + (real + (lambda (i) + (let ((parsed + (prefix + i + (lambda (i) + (sign + i + (lambda (i) + (digits + i + (lambda (i) + (point + i + (lambda (i) + (digits i exp))))))))))) + (and (list? parsed) + (apply + (lambda (sgn idigs fdigs exp) + (let* ((digs (string-append "0" idigs fdigs)) + (n (string-length digs))) + (let loop ((i 1) + (exp (+ exp (string-length idigs)))) + (if (and (< i n) + (char=? #\0 (string-ref digs i))) + (loop (+ i 1) (- exp 1)) + (list sgn (substring digs (- i 1) n) exp))))) + parsed))))) + (parse-error + (lambda () #f))) + (let ((realpart (real 0))) + (cond ((= iend n) realpart) + ((memv (string-ref str iend) '(#\+ #\-)) + (let ((complexpart (real iend))) + (and (= iend (- n 1)) + (char-ci=? #\i (string-ref str iend)) + (append realpart complexpart)))) + ((eqv? (string-ref str iend) #\@) + ;; Polar form: No point in parsing the angle ourselves, + ;; since some transcendental approximation is unavoidable. + (let ((num (string->number str))) + (and num + (let ((realpart + (stdio:parse-float + (number->string (real-part num)))) + (imagpart + (if (real? num) + '() + (stdio:parse-float + (number->string (imag-part num)))))) + (and realpart imagpart + (append realpart imagpart)))))) + (else #f)))))) + +;; STR is a digit string representing a floating point mantissa, STR must +;; begin with "0", after which a decimal point is understood. +;; The output is a digit string rounded to NDIGS digits after the decimal +;; point implied between chars 0 and 1. +;; If STRIP-0S is not #F then trailing zeros will be stripped from the result. +;; In this case, STRIP-0S should be the minimum number of digits required +;; after the implied decimal point. +(define (stdio:round-string str ndigs strip-0s) + (let* ((n (- (string-length str) 1)) + (res + (cond ((< ndigs 0) "") + ((= n ndigs) str) + ((< n ndigs) + (let ((zeropad (make-string + (max 0 (- (or strip-0s ndigs) n)) + (if (char-numeric? (string-ref str n)) + #\0 #\#)))) + (if (zero? (string-length zeropad)) + str + (string-append str zeropad)))) + (else + (let ((res (substring str 0 (+ ndigs 1))) + (dig (lambda (i) + (let ((c (string-ref str i))) + (if (char-numeric? c) + (string->number (string c)) + 0))))) + (let ((ldig (dig (+ 1 ndigs)))) + (if (or (> ldig 5) + (and (= ldig 5) + (let loop ((i (+ 2 ndigs))) + (if (> i n) (odd? (dig ndigs)) + (if (zero? (dig i)) + (loop (+ i 1)) + #t))))) + (let inc! ((i ndigs)) + (let ((d (dig i))) + (if (< d 9) + (string-set! res i + (string-ref + (number->string (+ d 1)) 0)) + (begin + (string-set! res i #\0) + (inc! (- i 1)))))))) + res))))) + (if strip-0s + (let loop ((i (- (string-length res) 1))) + (if (or (<= i strip-0s) + (not (char=? #\0 (string-ref res i)))) + (substring res 0 (+ i 1)) + (loop (- i 1)))) + res))) + +(define (stdio:iprintf out format-string . args) + (cond + ((not (equal? "" format-string)) + (let ((pos -1) + (fl (string-length format-string)) + (fc (string-ref format-string 0))) + + (define (advance) + (set! pos (+ 1 pos)) + (cond ((>= pos fl) (set! fc #f)) + (else (set! fc (string-ref format-string pos))))) + (define (must-advance) + (set! pos (+ 1 pos)) + (cond ((>= pos fl) (incomplete)) + (else (set! fc (string-ref format-string pos))))) + (define (end-of-format?) + (>= pos fl)) + (define (incomplete) + (slib:error 'printf "conversion specification incomplete" + format-string)) + (define (wna) + (slib:error 'printf "wrong number of arguments" + (length args) + format-string)) + + (let loop ((args args)) + (advance) + (cond + ((end-of-format?) + ;;(or (null? args) (wna)) ;Extra arguments are *not* a bug. + ) + ((eqv? #\\ fc);;Emulating C strings may not be a good idea. + (must-advance) + (and (case fc + ((#\n #\N) (out #\newline)) + ((#\t #\T) (out slib:tab)) + ;;((#\r #\R) (out #\return)) + ((#\f #\F) (out slib:form-feed)) + ((#\newline) #t) + (else (out fc))) + (loop args))) + ((eqv? #\% fc) + (must-advance) + (let ((left-adjust #f) ;- + (signed #f) ;+ + (blank #f) + (alternate-form #f) ;# + (leading-0s #f) ;0 + (width 0) + (precision -1) + (type-modifier #f) + (read-format-number + (lambda () + (cond + ((eqv? #\* fc) ; GNU extension + (must-advance) + (let ((ans (car args))) + (set! args (cdr args)) + ans)) + (else + (do ((c fc fc) + (accum 0 (+ (* accum 10) + (string->number (string c))))) + ((not (char-numeric? fc)) accum) + (must-advance))))))) + (define (pad pre . strs) + (let loop ((len (string-length pre)) + (ss strs)) + (cond ((>= len width) (apply string-append pre strs)) + ((null? ss) + (cond (left-adjust + (apply string-append + pre + (append strs + (list (make-string + (- width len) #\space))))) + (leading-0s + (apply string-append + pre + (make-string (- width len) #\0) + strs)) + (else + (apply string-append + (make-string (- width len) #\space) + pre strs)))) + (else + (loop (+ len (string-length (car ss))) (cdr ss)))))) + (define integer-convert + (lambda (s radix) + (cond ((not (negative? precision)) + (set! leading-0s #f) + (if (and (zero? precision) + (eqv? 0 s)) + (set! s "")))) + (set! s (cond ((symbol? s) (symbol->string s)) + ((number? s) (number->string s radix)) + ((or (not s) (null? s)) "0") + ((string? s) s) + (else "1"))) + (let ((pre (cond ((equal? "" s) "") + ((eqv? #\- (string-ref s 0)) + (set! s (substring s 1 (string-length s))) + "-") + (signed "+") + (blank " ") + (alternate-form + (case radix + ((8) "0") + ((16) "0x") + (else ""))) + (else "")))) + (pad pre + (if (< (string-length s) precision) + (make-string + (- precision (string-length s)) #\0) + "") + s)))) + (define (float-convert num fc) + (define (f digs exp strip-0s) + (let ((digs (stdio:round-string + digs (+ exp precision) (and strip-0s exp)))) + (cond ((>= exp 0) + (let* ((i0 (cond ((zero? exp) 0) + ((char=? #\0 (string-ref digs 0)) 1) + (else 0))) + (i1 (max 1 (+ 1 exp))) + (idigs (substring digs i0 i1)) + (fdigs (substring digs i1 + (string-length digs)))) + (cons idigs + (if (and (string=? fdigs "") + (not alternate-form)) + '() + (list "." fdigs))))) + ((zero? precision) + (list (if alternate-form "0." "0"))) + ((and strip-0s (string=? digs "") (list "0"))) + (else + (list "0." + (make-string (min precision (- -1 exp)) #\0) + digs))))) + (define (e digs exp strip-0s) + (let* ((digs (stdio:round-string + digs (+ 1 precision) (and strip-0s 0))) + (istrt (if (char=? #\0 (string-ref digs 0)) 1 0)) + (fdigs (substring + digs (+ 1 istrt) (string-length digs))) + (exp (if (zero? istrt) exp (- exp 1)))) + (list + (substring digs istrt (+ 1 istrt)) + (if (and (string=? fdigs "") (not alternate-form)) + "" ".") + fdigs + (if (char-upper-case? fc) "E" "e") + (if (negative? exp) "-" "+") + (if (< -10 exp 10) "0" "") + (number->string (abs exp))))) + (define (g digs exp) + (let ((strip-0s (not alternate-form))) + (set! alternate-form #f) + (cond ((<= (- 1 precision) exp precision) + (set! precision (- precision exp)) + (f digs exp strip-0s)) + (else + (set! precision (- precision 1)) + (e digs exp strip-0s))))) + (define (k digs exp sep) + (let* ((units '#("y" "z" "a" "f" "p" "n" "u" "m" "" + "k" "M" "G" "T" "P" "E" "Z" "Y")) + (base 8) ;index of "" + (uind (let ((i (if (negative? exp) + (quotient (- exp 3) 3) + (quotient (- exp 1) 3)))) + (and + (< -1 (+ i base) (vector-length units)) + i)))) + (cond (uind + (set! exp (- exp (* 3 uind))) + (set! precision (max 0 (- precision exp))) + (append + (f digs exp #f) + (list sep + (vector-ref units (+ uind base))))) + (else + (g digs exp))))) + + (cond ((negative? precision) + (set! precision 6)) + ((and (zero? precision) + (char-ci=? fc #\g)) + (set! precision 1))) + (let* ((str + (cond ((number? num) + (number->string (exact->inexact num))) + ((string? num) num) + ((symbol? num) (symbol->string num)) + (else "???"))) + (parsed (stdio:parse-float str))) + (letrec ((format-real + (lambda (signed? sgn digs exp . rest) + (if (null? rest) + (cons + (if (char=? #\- sgn) "-" + (if signed? "+" (if blank " " ""))) + (case fc + ((#\e #\E) (e digs exp #f)) + ((#\f #\F) (f digs exp #f)) + ((#\g #\G) (g digs exp)) + ((#\k) (k digs exp "")) + ((#\K) (k digs exp " ")))) + (append (format-real signed? sgn digs exp) + (apply format-real #t rest) + '("i")))))) + (if parsed + (apply pad (apply format-real signed parsed)) + (pad "???"))))) + (do () + ((case fc + ((#\-) (set! left-adjust #t) #f) + ((#\+) (set! signed #t) #f) + ((#\ ) (set! blank #t) #f) + ((#\#) (set! alternate-form #t) #f) + ((#\0) (set! leading-0s #t) #f) + (else #t))) + (must-advance)) + (cond (left-adjust (set! leading-0s #f))) + (cond (signed (set! blank #f))) + + (set! width (read-format-number)) + (cond ((negative? width) + (set! left-adjust #t) + (set! width (- width)))) + (cond ((eqv? #\. fc) + (must-advance) + (set! precision (read-format-number)))) + (case fc ;Ignore these specifiers + ((#\l #\L #\h) + (set! type-modifier fc) + (must-advance))) + + ;;At this point fc completely determines the format to use. + (if (null? args) + (if (memv (char-downcase fc) + '(#\c #\s #\a #\d #\i #\u #\o #\x #\b + #\f #\e #\g #\k)) + (wna))) + + (case fc + ;; only - is allowed between % and c + ((#\c #\C) ; C is enhancement + (and (out (string (car args))) (loop (cdr args)))) + + ;; only - flag, no type-modifiers + ((#\s #\S) ; S is enhancement + (let ((s (cond + ((symbol? (car args)) (symbol->string (car args))) + ((not (car args)) "(NULL)") + (else (car args))))) + (cond ((not (or (negative? precision) + (>= precision (string-length s)))) + (set! s (substring s 0 precision)))) + (and (out (cond + ((<= width (string-length s)) s) + (left-adjust + (string-append + s (make-string (- width (string-length s)) #\ ))) + (else + (string-append + (make-string (- width (string-length s)) + (if leading-0s #\0 #\ )) s)))) + (loop (cdr args))))) + + ;; SLIB extension + ((#\a #\A) ;#\a #\A are pretty-print + (let ((os "") (pr precision)) + (generic-write + (car args) (not alternate-form) #f + (cond ((and left-adjust (negative? pr)) + (set! pr 0) + (lambda (s) + (set! pr (+ pr (string-length s))) + (out s))) + (left-adjust + (lambda (s) + (define sl (- pr (string-length s))) + (set! pr (cond ((negative? sl) + (out (substring s 0 pr)) 0) + (else (out s) sl))) + (positive? sl))) + ((negative? pr) + (set! pr width) + (lambda (s) + (set! pr (- pr (string-length s))) + (cond ((not os) (out s)) + ((negative? pr) + (out os) + (set! os #f) + (out s)) + (else (set! os (string-append os s)))) + #t)) + (else + (lambda (s) + (define sl (- pr (string-length s))) + (cond ((negative? sl) + (set! os (string-append + os (substring s 0 pr)))) + (else (set! os (string-append os s)))) + (set! pr sl) + (positive? sl))))) + (cond ((and left-adjust (negative? precision)) + (cond + ((> width pr) (out (make-string (- width pr) #\ ))))) + (left-adjust + (cond + ((> width (- precision pr)) + (out (make-string (- width (- precision pr)) #\ ))))) + ((not os)) + ((<= width (string-length os)) (out os)) + (else (and (out (make-string + (- width (string-length os)) #\ )) + (out os))))) + (loop (cdr args))) + ((#\d #\D #\i #\I #\u #\U) + (and (out (integer-convert (car args) 10)) (loop (cdr args)))) + ((#\o #\O) + (and (out (integer-convert (car args) 8)) (loop (cdr args)))) + ((#\x #\X) + (and (out ((if (char-upper-case? fc) + string-upcase string-downcase) + (integer-convert (car args) 16))) + (loop (cdr args)))) + ((#\b #\B) + (and (out (integer-convert (car args) 2)) (loop (cdr args)))) + ((#\%) (and (out #\%) (loop args))) + ((#\f #\F #\e #\E #\g #\G #\k #\K) + (and (out (float-convert (car args) fc)) (loop (cdr args)))) + (else + (cond ((end-of-format?) (incomplete)) + (else (and (out #\%) (out fc) (out #\?) (loop args)))))))) + (else (and (out fc) (loop args))))))))) + +(define (stdio:fprintf port format . args) + (let ((cnt 0)) + (apply stdio:iprintf + (lambda (x) + (cond ((string? x) + (set! cnt (+ (string-length x) cnt)) (display x port) #t) + (else (set! cnt (+ 1 cnt)) (display x port) #t))) + format args) + cnt)) + +(define (stdio:printf format . args) + (apply stdio:fprintf (current-output-port) format args)) + +(define (stdio:sprintf str format . args) + (let* ((cnt 0) + (s (cond ((string? str) str) + ((number? str) (make-string str)) + ((not str) (make-string 100)) + (else (slib:error 'sprintf "first argument not understood" + str)))) + (end (string-length s))) + (apply stdio:iprintf + (lambda (x) + (cond ((string? x) + (if (or str (>= (- end cnt) (string-length x))) + (do ((lend (min (string-length x) (- end cnt))) + (i 0 (+ i 1))) + ((>= i lend)) + (string-set! s cnt (string-ref x i)) + (set! cnt (+ cnt 1))) + (let () + (set! s (string-append (substring s 0 cnt) x)) + (set! cnt (string-length s)) + (set! end cnt)))) + ((and str (>= cnt end))) + (else (cond ((and (not str) (>= cnt end)) + (set! s (string-append s (make-string 100))) + (set! end (string-length s)))) + (string-set! s cnt (if (char? x) x #\?)) + (set! cnt (+ cnt 1)))) + (not (and str (>= cnt end)))) + format + args) + (cond ((string? str) cnt) + ((eqv? end cnt) s) + (else (substring s 0 cnt))))) + +(define printf stdio:printf) +(define fprintf stdio:fprintf) +(define sprintf stdio:sprintf) + +;;(do ((i 0 (+ 1 i))) ((> i 50)) (printf "%s\n" (sprintf i "%#-13a:%#13a:%-13.8a:" "123456789" "123456789" "123456789"))) diff --git a/src/scm/startup.scm b/src/scm/startup.scm index de86f73d5d..12779b10a8 100644 --- a/src/scm/startup.scm +++ b/src/scm/startup.scm @@ -4,6 +4,9 @@ ;; None of these loads will be affected by any command line arguments ;; since arguments aren't parsed until gnc:main is executed. +(if gnc:*load-slib-backup* + (gnc:load "slib-backup.scm")) + (gnc:load "macros.scm") (gnc:load "config-var.scm") (gnc:load "utilities.scm") diff --git a/src/top-level.h b/src/top-level.h index 8a147d7b4e..26ae9f5ce1 100644 --- a/src/top-level.h +++ b/src/top-level.h @@ -38,6 +38,8 @@ #define HH_ADJBWIN "xacc-adjbwin.html" #define HH_MAIN "xacc-main.html" #define HH_GPL "xacc-gpl.html" +#define HH_GLOBPREFS "xacc-globalprefs.html" +#define HH_ACCEDIT "xacc-accountedit.html" /** STRUCTS *********************************************************/