From 737d33d7d846e6109b1d59d759611f241c63119c Mon Sep 17 00:00:00 2001 From: Dave Peticolas Date: Wed, 22 Nov 2000 03:46:20 +0000 Subject: [PATCH] More work on rebalancing. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3191 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/SplitLedger.c | 123 +++++++++++++++++++++++------------- src/SplitLedger.h | 4 +- src/engine/Account.c | 6 +- src/engine/Scrub.c | 104 +++++++++++++++++------------- src/engine/Scrub.h | 1 + src/engine/Transaction.c | 73 +++++++-------------- src/engine/Transaction.h | 7 -- src/engine/TransactionP.h | 3 - src/engine/kvp_doc.txt | 7 -- src/register/table-allgui.c | 12 ++-- src/register/table-allgui.h | 3 +- 11 files changed, 179 insertions(+), 164 deletions(-) diff --git a/src/SplitLedger.c b/src/SplitLedger.c index 765839da1f..06a6946c00 100644 --- a/src/SplitLedger.c +++ b/src/SplitLedger.c @@ -109,6 +109,7 @@ #include "FileDialog.h" #include "MultiLedger.h" #include "Refresh.h" +#include "Scrub.h" #include "SplitLedger.h" #include "global-options.h" #include "gnc-engine-util.h" @@ -896,7 +897,7 @@ LedgerMoveCursor (Table *table, VirtualLocation *p_new_virt_loc) /* This function determines if auto-completion is appropriate and, * if so, performs it. This should only be called by LedgerTraverse. */ -static void +static gboolean LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, VirtualLocation *p_new_virt_loc) { @@ -914,12 +915,12 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, /* auto-completion is only triggered by a tab out */ if (dir != GNC_TABLE_TRAVERSE_RIGHT) - return; + return FALSE; split = xaccSRGetCurrentSplit(reg); trans = xaccSRGetCurrentTrans(reg); if (trans == NULL) - return; + return FALSE; cursor_class = xaccSplitRegisterGetCurrentCursorClass(reg); cell_type = xaccSplitRegisterGetCurrentCellType(reg); @@ -934,28 +935,28 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, /* there must be a blank transaction * */ if (blank_trans == NULL) - return; + return FALSE; /* we must be on the blank split */ if (trans != blank_trans) - return; + return FALSE; /* and leaving the description cell */ if (cell_type != DESC_CELL) - return; + return FALSE; /* nothing but the date, num, and description should be changed */ if ((changed & ~(MOD_DATE | MOD_NUM | MOD_DESC)) != 0) - return; + return FALSE; /* and the description should be changed */ if ((changed & MOD_DESC) == 0) - return; + return FALSE; /* to a non-empty value */ desc = reg->descCell->cell.value; if ((desc == NULL) || (*desc == '\0')) - return; + return FALSE; /* find a transaction to auto-complete on */ if (info->default_source_account != NULL) @@ -968,7 +969,7 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, auto_trans = gnc_find_trans_in_reg_by_desc(reg, desc); if (auto_trans == NULL) - return; + return FALSE; xaccTransBeginEdit (trans, TRUE); gnc_copy_trans_onto_trans (auto_trans, trans, FALSE, FALSE); @@ -1042,24 +1043,24 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, /* we must be on a blank split of a transaction */ if (split != NULL) - return; + return FALSE; /* and leaving the memo cell */ if (cell_type != MEMO_CELL) - return; + return FALSE; - /* nothing but the action and memo should be changed */ - if ((changed & ~(MOD_ACTN | MOD_MEMO)) != 0) - return; + /* nothing but the action memo, and amounts should be changed */ + if ((changed & ~(MOD_ACTN | MOD_MEMO | MOD_AMNT)) != 0) + return FALSE; /* and the memo should be changed */ if ((changed & MOD_MEMO) == 0) - return; + return FALSE; /* to a non-empty value */ memo = reg->memoCell->cell.value; if ((memo == NULL) || (*memo == '\0')) - return; + return FALSE; /* if there is no price field, only auto-complete from splits with * a unit share price. */ @@ -1078,7 +1079,7 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, unit_price); if (auto_split == NULL) - return; + return FALSE; /* the auto-complete code below is taken from xaccSRGetEntryHandler */ @@ -1095,11 +1096,14 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, xaccBasicCellSetChanged(&(reg->xfrmCell->cell), TRUE); - amount = xaccSplitGetValue (auto_split); + if (!(changed & MOD_AMNT)) + { + amount = xaccSplitGetValue (auto_split); - xaccSetDebCredCellValue (reg->debitCell, reg->creditCell, amount); - xaccBasicCellSetChanged (&(reg->debitCell->cell), TRUE); - xaccBasicCellSetChanged (&(reg->creditCell->cell), TRUE); + xaccSetDebCredCellValue (reg->debitCell, reg->creditCell, amount); + xaccBasicCellSetChanged (&(reg->debitCell->cell), TRUE); + xaccBasicCellSetChanged (&(reg->creditCell->cell), TRUE); + } /* and refresh the gui */ gnc_table_refresh_gui (reg->table); @@ -1117,6 +1121,8 @@ LedgerAutoCompletion(SplitRegister *reg, gncTableTraversalDir dir, default: break; } + + return TRUE; } /* ======================================================== */ @@ -1162,12 +1168,8 @@ LedgerTraverse (Table *table, * auto-complete. */ if (!gnc_table_virtual_cell_out_of_bounds (table, virt_loc.vcell_loc)) { - if (virt_cell_loc_equal (virt_loc.vcell_loc, - table->current_cursor_loc.vcell_loc)) - { - LedgerAutoCompletion(reg, dir, p_new_virt_loc); + if (LedgerAutoCompletion(reg, dir, p_new_virt_loc)) return; - } } if (changed && (split == NULL) && (dir == GNC_TABLE_TRAVERSE_RIGHT)) @@ -1310,7 +1312,7 @@ xaccSRGetTrans (SplitRegister *reg, VirtualCellLocation vcell_loc) { Split *split; - if (reg == NULL) + if (!reg || !reg->table) return NULL; split = sr_get_split (reg, vcell_loc); @@ -2900,7 +2902,7 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) { gnc_numeric amount = xaccGetPriceCellValue(reg->sharesCell); gnc_numeric price = xaccGetPriceCellValue(reg->priceCell); - + DEBUG ("MOD_SHRS"); xaccSplitSetShareAmount (split, amount); @@ -2918,10 +2920,6 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) xaccSplitSetSharePrice (split, price); } - /* The AMNT and NAMNT updates only differ by sign. Basically, - * the split cursors show minus the quants that the single, - * double and transaction cursors show, and so when updates - * happen, the extra minus sign must also be handled. */ if (MOD_AMNT & changed) { gnc_numeric new_amount; @@ -2934,10 +2932,12 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split) DEBUG ("MOD_AMNT"); - /* FIXME : make sure amount gets updated? -- bg */ xaccSplitSetValue (split, new_amount); } - + + if ((MOD_AMNT | MOD_PRIC | MOD_SHRS) & changed) + xaccSplitScrubImbalance (split); + return refresh_accounts; } @@ -3028,19 +3028,53 @@ get_trans_total_balance (SplitRegister *reg, Transaction *trans) /* ======================================================== */ const char * -xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, - gpointer user_data) +xaccSRGetEntryHandler (VirtualLocation virt_loc, short _cell_type, + gboolean *changed, gpointer user_data) { - GUID *guid = vcell_data; CellType cell_type = _cell_type; SplitRegister *reg = user_data; const char *value = ""; Transaction *trans; Split *split; - split = xaccSplitLookup (guid); + if (changed) + *changed = FALSE; + + split = sr_get_split (reg, virt_loc.vcell_loc); if (split == NULL) - return value; + { + gnc_numeric imbalance; + + trans = xaccSRGetTrans (reg, virt_loc.vcell_loc); + imbalance = xaccTransGetImbalance (trans); + + if (gnc_numeric_zero_p (imbalance)) + return value; + + switch (cell_type) + { + case CRED_CELL: + case DEBT_CELL: + imbalance = gnc_numeric_neg (imbalance); + + if (gnc_numeric_negative_p (imbalance) && (cell_type == DEBT_CELL)) + return ""; + + if (gnc_numeric_positive_p (imbalance) && (cell_type == CRED_CELL)) + return ""; + + if (changed) + *changed = TRUE; + + imbalance = gnc_numeric_abs (imbalance); + + return xaccPrintAmount (imbalance, + gnc_split_value_print_info (split, FALSE)); + + default: + return value; + } + } trans = xaccSplitGetParent (split); @@ -3421,10 +3455,13 @@ xaccSRGetBGColorHandler (VirtualLocation virt_loc, gpointer user_data) case CURSOR_TYPE_SPLIT: { - Split *split = sr_get_split (reg, virt_loc.vcell_loc); - Transaction *trans = xaccSplitGetParent (split); + Transaction *trans; + gnc_numeric imbalance; - if (split && (split == xaccTransGetBalanceSplit (trans))) + trans = xaccSRGetTrans (reg, virt_loc.vcell_loc); + imbalance = xaccTransGetImbalance (trans); + + if (!gnc_numeric_zero_p (imbalance)) return 0xffff00; if (is_current) diff --git a/src/SplitLedger.h b/src/SplitLedger.h index 9d68065a1f..a17043e4d5 100644 --- a/src/SplitLedger.h +++ b/src/SplitLedger.h @@ -203,7 +203,9 @@ void xaccSetSplitRegisterColors (SplitRegisterColors reg_colors); void xaccSetSplitRegisterColorizeNegative (gboolean use_red); /* Private function, for MultiLedger.c only */ -const char * xaccSRGetEntryHandler (gpointer vcell_data, short _cell_type, +const char * xaccSRGetEntryHandler (VirtualLocation virt_loc, + short _cell_type, + gboolean *changed, gpointer user_data); guint32 xaccSRGetFGColorHandler (VirtualLocation virt_loc, gpointer user_data); diff --git a/src/engine/Account.c b/src/engine/Account.c index 5a57d35d6c..b07e53fa84 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -440,10 +440,10 @@ xaccAccountInsertSplit ( Account *acc, Split *split ) { { Account *oldacc; CHECK (acc); - + acc->balance_dirty = TRUE; acc->sort_dirty = TRUE; - + /* convert the split to the new account's denominators */ /* if the denominator can't be exactly converted, it's an error */ /* FIXME : need to enforce ordering of insertion/value */ @@ -462,7 +462,7 @@ xaccAccountInsertSplit ( Account *acc, Split *split ) { oldacc = split->acc; if (split->acc) xaccAccountRemoveSplit (split->acc, split); split->acc = acc; - + if(acc->editlevel == 1) { acc->splits = g_list_insert_sorted(acc->splits, split, split_sort_func); acc->sort_dirty = FALSE; diff --git a/src/engine/Scrub.c b/src/engine/Scrub.c index 34dc5564d4..3d4d165d51 100644 --- a/src/engine/Scrub.c +++ b/src/engine/Scrub.c @@ -144,26 +144,20 @@ xaccAccountScrubImbalance (Account *acc) void xaccTransScrubImbalance (Transaction *trans) { - Split *balance_split; + Split *balance_split = NULL; gnc_numeric imbalance; - gboolean trans_was_open; - gboolean had_balance_split; if (!trans) return; - trans_was_open = xaccTransIsOpen (trans); - - balance_split = xaccTransGetBalanceSplit (trans); - - had_balance_split = balance_split != NULL; - - if (!had_balance_split) { GList *node; Account *account; Account *peer = NULL; + for (node = trans->splits; node; node = node->next) + xaccSplitScrubImbalance (node->data); + imbalance = xaccTransGetImbalance (trans); if (gnc_numeric_zero_p (imbalance)) return; @@ -192,34 +186,18 @@ xaccTransScrubImbalance (Transaction *trans) xaccAccountInsertSplit (account, balance_split); xaccAccountCommitEdit (account); } - else - { - const gnc_commodity * currency = xaccTransFindCommonCurrency (trans); - imbalance = xaccSplitsComputeValue (trans->splits, - balance_split, currency); - if (gnc_numeric_zero_p (imbalance)) /* balances without balance split */ - { - if (!trans_was_open) - xaccTransBeginEdit (trans, TRUE); - - xaccSplitDestroy (balance_split); - xaccTransSetBalanceSplit (trans, NULL); - - if (!trans_was_open) - xaccTransCommitEdit (trans); - - return; - } - - imbalance = xaccTransGetImbalance (trans); - } + PWARN ("unbalanced transaction: %s", + guid_to_string (xaccTransGetGUID (trans))); { const gnc_commodity *common_currency; const gnc_commodity *commodity; + gboolean trans_was_open; Account *account; + trans_was_open = xaccTransIsOpen (trans); + if (!trans_was_open) xaccTransBeginEdit (trans, TRUE); @@ -235,30 +213,68 @@ xaccTransScrubImbalance (Transaction *trans) xaccSplitSetValue (balance_split, new_value); } - else + + commodity = xaccAccountGetSecurity (account); + if (gnc_commodity_equiv (common_currency, commodity)) { - commodity = xaccAccountGetSecurity (account); - if (gnc_commodity_equiv (common_currency, commodity)) - { - gnc_numeric new_share_amount = xaccSplitGetShareAmount (balance_split); + gnc_numeric new_share_amount = xaccSplitGetShareAmount (balance_split); - new_share_amount = gnc_numeric_sub_fixed (new_share_amount, imbalance); + new_share_amount = gnc_numeric_sub_fixed (new_share_amount, imbalance); - xaccSplitSetShareAmount (balance_split, new_share_amount); - } + xaccSplitSetShareAmount (balance_split, new_share_amount); } - if (!had_balance_split) - { - xaccTransAppendSplit (trans, balance_split); - xaccTransSetBalanceSplit (trans, balance_split); - } + xaccTransAppendSplit (trans, balance_split); if (!trans_was_open) xaccTransCommitEdit (trans); } } +void xaccSplitScrubImbalance (Split *split) +{ + Account *account; + Transaction *trans; + gboolean trans_was_open; + int scu; + + if (!split) + return; + + trans = xaccSplitGetParent (split); + if (!trans) + return; + + account = xaccSplitGetAccount (split); + if (!account) + return; + + if (!gnc_commodity_equiv (xaccAccountGetCurrency (account), + xaccAccountGetSecurity (account))) + return; + + scu = MIN (xaccAccountGetCurrencySCU (account), + xaccAccountGetSecuritySCU (account)); + + if (gnc_numeric_same (xaccSplitGetShareAmount (split), + xaccSplitGetValue (split), + scu, GNC_RND_ROUND)) + return; + + PWARN ("split with mismatched values: %s", + guid_to_string (xaccSplitGetGUID (split))); + + trans_was_open = xaccTransIsOpen (trans); + + if (!trans_was_open) + xaccTransBeginEdit (trans, TRUE); + + xaccSplitSetShareAmount (split, xaccSplitGetValue (split)); + + if (!trans_was_open) + xaccTransCommitEdit (trans); +} + /* ================================================================ */ static Account * diff --git a/src/engine/Scrub.h b/src/engine/Scrub.h index 55d0d414c9..08732f179c 100644 --- a/src/engine/Scrub.h +++ b/src/engine/Scrub.h @@ -62,6 +62,7 @@ void xaccGroupScrubOrphans (AccountGroup *grp); * is created to offset this amount and is added to an "imbalance" * account. */ +void xaccSplitScrubImbalance (Split *split); void xaccTransScrubImbalance (Transaction *trans); void xaccAccountScrubImbalance (Account *acc); void xaccAccountTreeScrubImbalance (Account *acc); diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 32c0f6e0e1..984fd31166 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -369,7 +369,8 @@ xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, if (!s) return; MARK_SPLIT(s); - s->damount = amt; + + s->damount = gnc_numeric_convert(amt, get_security_denom(s), GNC_RND_ROUND);; s->value = gnc_numeric_mul(s->damount, price, get_currency_denom(s), GNC_RND_ROUND); @@ -424,9 +425,10 @@ DxaccSplitSetShareAmount (Split *s, double damt) { void xaccSplitSetShareAmount (Split *s, gnc_numeric amt) { if(!s) return; + MARK_SPLIT(s); - - s->damount = amt; + + s->damount = gnc_numeric_convert(amt, get_security_denom(s), GNC_RND_ROUND); xaccSplitRebalance (s); } @@ -466,7 +468,7 @@ xaccSplitSetValue (Split *s, gnc_numeric amt) { if(!s) return; MARK_SPLIT(s); - s->value = amt; + s->value = gnc_numeric_convert(amt, get_currency_denom(s), GNC_RND_ROUND);; xaccSplitRebalance (s); } @@ -970,55 +972,13 @@ xaccSplitsComputeValue (GList *splits, Split * skip_me, gnc_numeric xaccTransGetImbalance (Transaction * trans) { - const gnc_commodity * currency = xaccTransFindCommonCurrency (trans); - gnc_numeric imbal = xaccSplitsComputeValue (trans->splits, NULL, currency); - return imbal; -} - -Split * -xaccTransGetBalanceSplit (Transaction *trans) -{ - Split *split; - kvp_value *kvp; - GUID *guid; + const gnc_commodity * currency; if (!trans) - return NULL; + return gnc_numeric_zero (); - kvp = kvp_frame_get_slot (xaccTransGetSlots (trans), "balance-split"); - if (!kvp) - return NULL; - - guid = kvp_value_get_guid (kvp); - if (!guid) - return NULL; - - split = xaccSplitLookup (guid); - if (g_list_find (trans->splits, split)) - return split; - - xaccTransSetBalanceSplit (trans, NULL); - - return NULL; -} - -void -xaccTransSetBalanceSplit (Transaction *trans, Split *split) -{ - kvp_value *new_value; - - if (!trans) - return; - - if (split) - new_value = kvp_value_new_guid (xaccSplitGetGUID (split)); - else - new_value = NULL; - - kvp_frame_set_slot(xaccTransGetSlots (trans), "balance-split", new_value); - - if (new_value) - kvp_value_delete(new_value); + currency = xaccTransFindCommonCurrency (trans); + return xaccSplitsComputeValue (trans->splits, NULL, currency); } /********************************************************************\ @@ -1128,6 +1088,8 @@ xaccTransFindCommonCurrency (Transaction *trans) const gnc_commodity * ra, * rb; Split *split; + if (!trans) return NULL; + if (trans->splits == NULL) return NULL; split = trans->splits->data; @@ -1143,6 +1105,7 @@ xaccTransFindCommonCurrency (Transaction *trans) const gnc_commodity * xaccTransIsCommonCurrency (Transaction *trans, const gnc_commodity * ra) { + if (!trans) return NULL; return FindCommonCurrency (trans->splits, ra, NULL); } @@ -1151,6 +1114,7 @@ xaccTransIsCommonExclSCurrency (Transaction *trans, const gnc_commodity * ra, Split *excl_split) { + if (!trans) return NULL; return FindCommonExclSCurrency (trans->splits, ra, NULL, excl_split); } @@ -1166,6 +1130,8 @@ xaccTransIsCommonExclSCurrency (Transaction *trans, static void xaccTransRebalance (Transaction * trans) { + return; + if (!trans || !trans->splits) return; @@ -1179,6 +1145,13 @@ xaccSplitRebalance (Split *split) gnc_numeric value; const gnc_commodity * base_currency = NULL; +#if 0 + if (split->acc && + gnc_commodity_equiv (xaccAccountGetCurrency (split->acc), + xaccAccountGetSecurity (split->acc))) + split->damount = split->value; +#endif + /* let's see how we do without all this stuff */ return; diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index 6f8c073fd0..e3beab39de 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -296,13 +296,6 @@ xaccTransIsCommonExclSCurrency (Transaction *trans, */ gnc_numeric xaccTransGetImbalance (Transaction * trans); -/* The xaccTransGetBalanceSplit method returns the 'balance' split of - * a transaction or NULL if there is no balance split. The balance - * split is an 'artificial' split created to make the transaction - * balance. This split is automatically managed by the rebalancing - * routines and is created and destroyed as needed. */ -Split * xaccTransGetBalanceSplit (Transaction *trans); - /* ------------- splits --------------- */ Split * xaccMallocSplit (void); diff --git a/src/engine/TransactionP.h b/src/engine/TransactionP.h index 63887739c5..0a871ef47a 100644 --- a/src/engine/TransactionP.h +++ b/src/engine/TransactionP.h @@ -197,7 +197,4 @@ void xaccFreeSplit (Split *split); /* frees memory */ gnc_numeric xaccSplitsComputeValue (GList *splits, Split * skip_me, const gnc_commodity * base_currency); -/* Set the balance split of a transaction. */ -void xaccTransSetBalanceSplit (Transaction *trans, Split *split); - #endif /* __XACC_TRANSACTION_P_H__ */ diff --git a/src/engine/kvp_doc.txt b/src/engine/kvp_doc.txt index 278b857239..a3a6fd8e64 100644 --- a/src/engine/kvp_doc.txt +++ b/src/engine/kvp_doc.txt @@ -36,13 +36,6 @@ Please put the keys in alphabetical order. -------------------------------------------------------------------------- -Name: balance-split -Type: GUID -Entities: Transaction -Use: xaccTransGetBalanceSplit, xaccTransSetBalanceSplit - Store the GUID of the 'balance' split of a transaction. - This split, if present, ensures the transaction is balanced. - Name: memo Type: string Entities: Split diff --git a/src/register/table-allgui.c b/src/register/table-allgui.c index c8ec350f2e..ff651de798 100644 --- a/src/register/table-allgui.c +++ b/src/register/table-allgui.c @@ -167,7 +167,8 @@ gnc_table_get_header_cell (Table *table) /* ==================================================== */ static const char * -gnc_table_get_entry_internal (Table *table, VirtualLocation virt_loc) +gnc_table_get_entry_internal (Table *table, VirtualLocation virt_loc, + gboolean *changed) { VirtualCell *vcell; CellBlockCell *cb_cell; @@ -184,7 +185,7 @@ gnc_table_get_entry_internal (Table *table, VirtualLocation virt_loc) if (cb_cell->cell_type < 0) return ""; - return table->entry_handler (vcell->vcell_data, cb_cell->cell_type, + return table->entry_handler (virt_loc, cb_cell->cell_type, changed, table->handler_user_data); } @@ -216,7 +217,7 @@ gnc_table_get_entry (Table *table, VirtualLocation virt_loc) return cb_cell->cell->value; } - return table->entry_handler (vcell->vcell_data, cb_cell->cell_type, + return table->entry_handler (virt_loc, cb_cell->cell_type, NULL, table->handler_user_data); } @@ -602,12 +603,13 @@ gnc_table_move_cursor_internal (Table *table, if (XACC_CELL_ALLOW_SHADOW & (cell->input_output)) { const char *entry; + gboolean changed = FALSE; - entry = gnc_table_get_entry_internal (table, virt_loc); + entry = gnc_table_get_entry_internal (table, virt_loc, &changed); xaccSetBasicCellValue (cell, entry); - cell->changed = 0; + cell->changed = changed ? GNC_CELL_CHANGED : 0; } } } diff --git a/src/register/table-allgui.h b/src/register/table-allgui.h index b73d5a3146..ee3ced22ed 100644 --- a/src/register/table-allgui.h +++ b/src/register/table-allgui.h @@ -150,8 +150,9 @@ typedef void (*TableSetHelpFunc) (Table *table, typedef void (*TableDestroyFunc) (Table *table); -typedef const char * (*TableGetEntryHandler) (gpointer vcell_data, +typedef const char * (*TableGetEntryHandler) (VirtualLocation virt_loc, short cell_type, + gboolean *changed, gpointer user_data); typedef guint32 (*TableGetFGColorHandler) (VirtualLocation virt_loc,