Files
gnucash/src/app-utils/gnc-ui-util.c

2142 lines
55 KiB
C
Raw Normal View History

/********************************************************************\
* gnc-ui-util.c -- utility functions for the GnuCash UI *
* Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include "config.h"
#include <glib.h>
#include <glib/gi18n.h>
#include <libguile.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Account.h"
#include "gnc-book.h"
#include "gnc-component-manager.h"
#include "qof.h"
#include "gnc-engine.h"
#include "gnc-euro.h"
#include "gnc-gconf-utils.h"
#include "gnc-hooks.h"
#include "gnc-module.h"
#include "gnc-ui-util.h"
#include "Group.h"
#include "Transaction.h"
#include "guile-mappings.h"
#include "gnc-session.h"
#define KEY_CURRENCY_CHOICE "currency_choice"
#define KEY_CURRENCY_OTHER "currency_other"
#define KEY_REVERSED_ACCOUNTS "reversed_accounts"
static QofLogModule log_module = GNC_MOD_GUI;
static gboolean auto_decimal_enabled = FALSE;
static int auto_decimal_places = 2; /* default, can be changed */
static gboolean reverse_balance_inited = FALSE;
static gboolean reverse_type[NUM_ACCOUNT_TYPES];
/* Cache currency ISO codes and only look them up in gconf when
* absolutely necessary. Can't cache a pointer to the data structure
* as that will change any time the book changes. */
static gchar *user_default_currency = NULL;
static gchar *user_report_currency = NULL;
/********************************************************************\
* gnc_configure_account_separator *
* updates the current account separator character *
* *
* Args: none *
\*******************************************************************/
static void
gnc_configure_account_separator (void)
{
const gchar *separator;
char *string;
string = gnc_gconf_get_string(GCONF_GENERAL, KEY_ACCOUNT_SEPARATOR, NULL);
if (!string || !*string || safe_strcmp(string, "colon") == 0)
separator = ":";
else if (safe_strcmp(string, "slash") == 0)
separator = "/";
else if (safe_strcmp(string, "backslash") == 0)
separator = "\\";
else if (safe_strcmp(string, "dash") == 0)
separator = "-";
else if (safe_strcmp(string, "period") == 0)
separator = ".";
else
separator = string;
gnc_set_account_separator(separator);
if (string != NULL)
free(string);
}
static void
gnc_configure_reverse_balance (void)
{
gchar *choice;
gint i;
for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
reverse_type[i] = FALSE;
choice = gnc_gconf_get_string(GCONF_GENERAL, KEY_REVERSED_ACCOUNTS, NULL);
if (safe_strcmp (choice, "none") == 0)
{
}
else if (safe_strcmp (choice, "income_expense") == 0)
{
reverse_type[ACCT_TYPE_INCOME] = TRUE;
reverse_type[ACCT_TYPE_EXPENSE] = TRUE;
}
else
{
if (safe_strcmp (choice, "credit") != 0)
PERR("bad value '%s'", choice ? choice : "(null)");
reverse_type[ACCT_TYPE_LIABILITY] = TRUE;
reverse_type[ACCT_TYPE_PAYABLE] = TRUE;
reverse_type[ACCT_TYPE_EQUITY] = TRUE;
reverse_type[ACCT_TYPE_INCOME] = TRUE;
reverse_type[ACCT_TYPE_CREDIT] = TRUE;
}
if (choice != NULL)
free (choice);
}
static void
gnc_reverse_balance_init (void)
{
gnc_configure_reverse_balance ();
reverse_balance_inited = TRUE;
}
gboolean
gnc_reverse_balance_type (GNCAccountType type)
{
if ((type < 0) || (type >= NUM_ACCOUNT_TYPES))
return FALSE;
if (!reverse_balance_inited)
gnc_reverse_balance_init ();
return reverse_type[type];
}
gboolean
gnc_reverse_balance (const Account *account)
{
int type;
if (account == NULL)
return FALSE;
type = xaccAccountGetType (account);
if ((type < 0) || (type >= NUM_ACCOUNT_TYPES))
return FALSE;
if (!reverse_balance_inited)
gnc_reverse_balance_init ();
return reverse_type[type];
}
gchar *
gnc_get_default_directory (const gchar *gconf_section)
{
gchar *dir;
dir = gnc_gconf_get_string (gconf_section, KEY_LAST_PATH, NULL);
if (!dir)
dir = g_strdup (g_get_home_dir ());
return dir;
}
void
gnc_set_default_directory (const gchar *gconf_section, const gchar *directory)
{
gnc_gconf_set_string(gconf_section, KEY_LAST_PATH, directory, NULL);
}
QofBook *
gnc_get_current_book (void)
{
return qof_session_get_book (gnc_get_current_session ());
}
AccountGroup *
gnc_get_current_group (void)
{
return gnc_book_get_group (gnc_get_current_book ());
}
gnc_commodity_table *
gnc_get_current_commodities (void)
{
return gnc_book_get_commodity_table (gnc_get_current_book ());
}
/*
* This is a wrapper routine around an xaccGetBalanceInCurrency
* function that handles additional needs of the gui.
*
* @param fn The underlying function in Account.c to call to retrieve
* a specific balance from the account.
* @param account The account to retrieve data about.
* @param recurse Include all sub-accounts of this account.
* @param negative An indication of whether or not the returned value
* is negative. This can be used by the caller to
* easily decode whether or not to color the output.
* @param commodity The commodity in which the account balance should
* be returned. If NULL, the value will be returned in
* the commodity of the account. This is normally used
* to specify a currency, which forces the conversion
* of things like stock account values from share
* values to an amount the requested currency.
*/
gnc_numeric
gnc_ui_account_get_balance_full (xaccGetBalanceInCurrencyFn fn,
const Account *account,
gboolean recurse,
gboolean *negative,
const gnc_commodity *commodity)
{
gnc_numeric balance;
balance = fn(account, commodity, recurse);
/* reverse sign if needed */
if (gnc_reverse_balance (account))
balance = gnc_numeric_neg (balance);
/* Record whether the balance is negative. */
if (negative)
*negative = gnc_numeric_negative_p(balance);
return balance;
}
/*
* This routine retrives the total balance in an account, possibly
* including all sub-accounts under the specified account.
*/
gnc_numeric
gnc_ui_account_get_balance (const Account *account, gboolean recurse)
{
return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency,
account, recurse, NULL, NULL);
}
/*
* This routine retrives the total balance in an account converted to
* a given currency, possibly including all sub-accounts under the
* specified account.
*/
gnc_numeric
gnc_ui_account_get_balance_in_currency (const Account *account,
const gnc_commodity *currency,
gboolean recurse)
{
return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency,
account, recurse, NULL, currency);
}
/*
* This routine retrives the reconciled balance in an account,
* possibly including all sub-accounts under the specified account.
*/
gnc_numeric
gnc_ui_account_get_reconciled_balance (const Account *account,
gboolean recurse)
{
return gnc_ui_account_get_balance_full (xaccAccountGetReconciledBalanceInCurrency,
account, recurse, NULL, NULL);
}
/**
* Wrapper around gnc_ui_account_get_balance_full that converts
* the resulting number to a character string. The number is
* formatted according to the specification of the account currency.
* The caller is responsible for g_free'ing the returned memory.
*
* @param fn The underlying function in Account.c to call to retrieve
* a specific balance from the account.
* @param account The account to retrieve data about.
* @param recurse Include all sub-accounts of this account.
* @param negative An indication of whether or not the returned value
* is negative. This can be used by the caller to
* easily decode whether or not to color the output.
*/
gchar *
gnc_ui_account_get_print_balance (xaccGetBalanceInCurrencyFn fn,
const Account *account,
gboolean recurse,
gboolean *negative)
{
GNCPrintAmountInfo print_info;
gnc_numeric balance;
balance = gnc_ui_account_get_balance_full(fn, account, recurse,
negative, NULL);
print_info = gnc_account_print_info(account, TRUE);
return g_strdup(xaccPrintAmount(balance, print_info));
}
/**
* Wrapper around gnc_ui_account_get_balance_full that converts
* the resulting number to a character string. The number is
* formatted according to the specification of the default reporting
* currency.
*
* @param fn The underlying function in Account.c to call to retrieve
* a specific balance from the account.
* @param account The account to retrieve data about.
* @param recurse Include all sub-accounts of this account.
* @param negative An indication of whether or not the returned value
* is negative. This can be used by the caller to
* easily decode whether or not to color the output.
*/
gchar *
gnc_ui_account_get_print_report_balance (xaccGetBalanceInCurrencyFn fn,
const Account *account,
gboolean recurse,
gboolean *negative)
{
GNCPrintAmountInfo print_info;
gnc_numeric balance;
gnc_commodity *report_commodity;
report_commodity = gnc_default_report_currency();
balance = gnc_ui_account_get_balance_full(fn, account, recurse,
negative, report_commodity);
print_info = gnc_commodity_print_info(report_commodity, TRUE);
return g_strdup(xaccPrintAmount(balance, print_info));
}
gnc_numeric
gnc_ui_account_get_balance_as_of_date (Account *account,
time_t date,
gboolean include_children)
{
gnc_numeric balance;
gnc_commodity *currency;
if (account == NULL)
return gnc_numeric_zero ();
currency = xaccAccountGetCommodity (account);
balance = xaccAccountGetBalanceAsOfDate (account, date);
if (include_children)
{
AccountGroup *children_group;
GList *children, *node;
children_group = xaccAccountGetChildren (account);
children = xaccGroupGetSubAccounts (children_group);
for (node = children; node; node = node->next)
{
Account *child;
gnc_commodity *child_currency;
gnc_numeric child_balance;
child = node->data;
child_currency = xaccAccountGetCommodity (child);
child_balance = xaccAccountGetBalanceAsOfDate (child, date);
child_balance = xaccAccountConvertBalanceToCurrency (child,
child_balance, child_currency, currency);
balance = gnc_numeric_add_fixed (balance, child_balance);
}
}
/* reverse sign if needed */
if (gnc_reverse_balance (account))
balance = gnc_numeric_neg (balance);
return balance;
}
/* Caller is responsible for g_free'ing returned memory */
char *
gnc_ui_account_get_tax_info_string (const Account *account)
{
static SCM get_form = SCM_UNDEFINED;
static SCM get_desc = SCM_UNDEFINED;
GNCAccountType atype;
const char *code;
SCM category;
SCM code_scm;
const gchar *form, *desc;
SCM scm;
if (get_form == SCM_UNDEFINED)
{
GNCModule module;
/* load the tax info */
#ifdef LOCALE_SPECIFIC_TAX
const char *thislocale = setlocale(LC_ALL, NULL);
/* This is a very simple hack that loads the (new, special) German
tax definition file in a German locale, or (default) loads the
previous US tax file. */
gboolean is_de_DE = (strncmp(thislocale, "de_DE", 5) == 0);
#else
gboolean is_de_DE = FALSE;
#endif /* LOCALE_SPECIFIC_TAX */
const char *tax_module = is_de_DE ?
"gnucash/tax/de_DE" :
"gnucash/tax/us";
module = gnc_module_load ((char *)tax_module, 0);
g_return_val_if_fail (module, NULL);
get_form = scm_c_eval_string ("(false-if-exception gnc:txf-get-form)");
get_desc = scm_c_eval_string ("(false-if-exception gnc:txf-get-description)");
}
g_return_val_if_fail (SCM_PROCEDUREP (get_form), NULL);
g_return_val_if_fail (SCM_PROCEDUREP (get_desc), NULL);
if (!account)
return NULL;
if (!xaccAccountGetTaxRelated (account))
return NULL;
atype = xaccAccountGetType (account);
if (atype != ACCT_TYPE_INCOME && atype != ACCT_TYPE_EXPENSE)
return NULL;
code = xaccAccountGetTaxUSCode (account);
if (!code)
return NULL;
category = scm_c_eval_string (atype == ACCT_TYPE_INCOME ?
"txf-income-categories" :
"txf-expense-categories");
code_scm = scm_str2symbol (code);
scm = scm_call_2 (get_form, category, code_scm);
if (!SCM_STRINGP (scm))
return NULL;
form = SCM_STRING_CHARS (scm);
if (!form)
return NULL;
scm = scm_call_2 (get_desc, category, code_scm);
if (!SCM_STRINGP (scm))
return NULL;
desc = SCM_STRING_CHARS (scm);
if (!desc)
return NULL;
return g_strdup_printf ("%s %s", form, desc);
}
static const char *
string_after_colon (const char *msgstr)
{
const char *string_at_colon;
g_assert(msgstr);
string_at_colon = strchr(msgstr, ':');
if (string_at_colon)
return string_at_colon + 1;
else
/* No colon found; we assume the translation contains only the
part after the colon, similar to the disambiguation prefixes */
return msgstr;
}
/********************************************************************\
* gnc_get_reconcile_str *
* return the i18n'd string for the given reconciled flag *
* *
* Args: reconciled_flag - the flag to stringize *
* Returns: the i18n'd reconciled string *
\********************************************************************/
const char *
gnc_get_reconcile_str (char reconciled_flag)
{
switch (reconciled_flag)
{
/* Translators: For the following strings, the single letters
after the colon are abbreviations of the word before the
colon. You should only translate the letter *after* the colon. */
case NREC: return string_after_colon(_("not cleared:n"));
/* Translators: Please only translate the letter *after* the colon. */
case CREC: return string_after_colon(_("cleared:c"));
/* Translators: Please only translate the letter *after* the colon. */
case YREC: return string_after_colon(_("reconciled:y"));
/* Translators: Please only translate the letter *after* the colon. */
case FREC: return string_after_colon(_("frozen:f"));
/* Translators: Please only translate the letter *after* the colon. */
case VREC: return string_after_colon(_("void:v"));
default:
PERR("Bad reconciled flag\n");
return NULL;
}
}
/********************************************************************\
* gnc_get_reconcile_valid_flags *
* return a string containing the list of reconciled flags *
* *
* Returns: the i18n'd reconciled flags string *
\********************************************************************/
const char *
gnc_get_reconcile_valid_flags (void)
{
static const char flags[] = { NREC, CREC, YREC, FREC, VREC, 0 };
return flags;
}
/********************************************************************\
* gnc_get_reconcile_flag_order *
* return a string containing the reconciled-flag change order *
* *
* Args: reconciled_flag - the flag to stringize *
* Returns: the i18n'd reconciled string *
\********************************************************************/
const char *
gnc_get_reconcile_flag_order (void)
{
static const char flags[] = { NREC, CREC, 0 };
return flags;
}
static const char *
equity_base_name (GNCEquityType equity_type)
{
switch (equity_type)
{
case EQUITY_OPENING_BALANCE:
return N_("Opening Balances");
case EQUITY_RETAINED_EARNINGS:
return N_("Retained Earnings");
default:
return NULL;
}
}
Account *
gnc_find_or_create_equity_account (AccountGroup *group,
GNCEquityType equity_type,
gnc_commodity *currency,
QofBook *book)
{
Account *parent;
Account *account;
gboolean name_exists;
gboolean base_name_exists;
const char *base_name;
char *name;
g_return_val_if_fail (equity_type >= 0, NULL);
g_return_val_if_fail (equity_type < NUM_EQUITY_TYPES, NULL);
g_return_val_if_fail (currency != NULL, NULL);
g_return_val_if_fail (group != NULL, NULL);
base_name = equity_base_name (equity_type);
account = xaccGetAccountFromName (group, base_name);
if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
account = NULL;
if (!account)
{
base_name = base_name && *base_name ? _(base_name) : "";
account = xaccGetAccountFromName (group, base_name);
if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
account = NULL;
}
base_name_exists = (account != NULL);
if (account &&
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
return account;
name = g_strconcat (base_name, " - ",
gnc_commodity_get_mnemonic (currency), NULL);
account = xaccGetAccountFromName (group, name);
if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
account = NULL;
name_exists = (account != NULL);
if (account &&
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
return account;
/* Couldn't find one, so create it */
if (name_exists && base_name_exists)
{
PWARN ("equity account with unexpected currency");
g_free (name);
return NULL;
}
if (!base_name_exists &&
gnc_commodity_equiv (currency, gnc_default_currency ()))
{
g_free (name);
name = g_strdup (base_name);
}
parent = xaccGetAccountFromName (group, _("Equity"));
if (parent && xaccAccountGetType (parent) != ACCT_TYPE_EQUITY)
parent = NULL;
account = xaccMallocAccount (book);
xaccAccountBeginEdit (account);
xaccAccountSetName (account, name);
xaccAccountSetType (account, ACCT_TYPE_EQUITY);
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
xaccAccountSetCommodity (account, currency);
if (parent)
{
xaccAccountBeginEdit (parent);
xaccAccountInsertSubAccount (parent, account);
xaccAccountCommitEdit (parent);
}
else
xaccGroupInsertAccount (group, account);
xaccAccountCommitEdit (account);
g_free (name);
return account;
}
gboolean
gnc_account_create_opening_balance (Account *account,
gnc_numeric balance,
time_t date,
QofBook *book)
{
Account *equity_account;
Transaction *trans;
Split *split;
if (gnc_numeric_zero_p (balance))
return TRUE;
g_return_val_if_fail (account != NULL, FALSE);
equity_account =
gnc_find_or_create_equity_account (xaccAccountGetRoot (account),
EQUITY_OPENING_BALANCE,
xaccAccountGetCommodity (account),
book);
if (!equity_account)
return FALSE;
xaccAccountBeginEdit (account);
xaccAccountBeginEdit (equity_account);
trans = xaccMallocTransaction (book);
xaccTransBeginEdit (trans);
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
xaccTransSetCurrency (trans, xaccAccountGetCommodity (account));
xaccTransSetDateSecs (trans, date);
xaccTransSetDescription (trans, _("Opening Balance"));
split = xaccMallocSplit (book);
xaccTransAppendSplit (trans, split);
xaccAccountInsertSplit (account, split);
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
xaccSplitSetAmount (split, balance);
xaccSplitSetValue (split, balance);
balance = gnc_numeric_neg (balance);
split = xaccMallocSplit (book);
xaccTransAppendSplit (trans, split);
xaccAccountInsertSplit (equity_account, split);
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
xaccSplitSetAmount (split, balance);
xaccSplitSetValue (split, balance);
xaccTransCommitEdit (trans);
xaccAccountCommitEdit (equity_account);
xaccAccountCommitEdit (account);
return TRUE;
}
char *
gnc_account_get_full_name (const Account *account)
{
if (!account) return NULL;
return xaccAccountGetFullName (account);
}
static void
gnc_lconv_set_utf8 (char **p_value, char *default_value)
{
char *value = *p_value;
if ((value == NULL) || (value[0] == 0))
*p_value = default_value;
*p_value = g_locale_to_utf8 (*p_value, -1, NULL, NULL, NULL);
// FIXME: Do we really need to make a copy here ?
//*p_value = g_strdup (*p_value);
}
static void
gnc_lconv_set_char (char *p_value, char default_value)
{
if ((p_value != NULL) && (*p_value == CHAR_MAX))
*p_value = default_value;
}
struct lconv *
gnc_localeconv (void)
{
static struct lconv lc;
static gboolean lc_set = FALSE;
if (lc_set)
return &lc;
lc = *localeconv();
gnc_lconv_set_utf8(&lc.decimal_point, ".");
gnc_lconv_set_utf8(&lc.thousands_sep, ",");
gnc_lconv_set_utf8(&lc.grouping, "\003");
gnc_lconv_set_utf8(&lc.int_curr_symbol, "USD ");
gnc_lconv_set_utf8(&lc.currency_symbol, "$");
gnc_lconv_set_utf8(&lc.mon_decimal_point, ".");
gnc_lconv_set_utf8(&lc.mon_thousands_sep, ",");
gnc_lconv_set_utf8(&lc.mon_grouping, "\003");
gnc_lconv_set_utf8(&lc.negative_sign, "-");
gnc_lconv_set_char(&lc.frac_digits, 2);
gnc_lconv_set_char(&lc.int_frac_digits, 2);
gnc_lconv_set_char(&lc.p_cs_precedes, 1);
gnc_lconv_set_char(&lc.p_sep_by_space, 0);
gnc_lconv_set_char(&lc.n_cs_precedes, 1);
gnc_lconv_set_char(&lc.n_sep_by_space, 0);
gnc_lconv_set_char(&lc.p_sign_posn, 1);
gnc_lconv_set_char(&lc.n_sign_posn, 1);
lc_set = TRUE;
return &lc;
}
const char *
gnc_locale_default_iso_currency_code (void)
{
static char *code = NULL;
struct lconv *lc;
if (code)
return code;
lc = gnc_localeconv ();
code = g_strdup (lc->int_curr_symbol);
/* The int_curr_symbol includes a space at the end! Note: you
* can't just change "USD " to "USD" in gnc_localeconv, because
* that is only used if int_curr_symbol was not defined in the
* current locale. If it was, it will have the space! */
g_strstrip (code);
return code;
}
gnc_commodity *
gnc_locale_default_currency_nodefault (void)
{
gnc_commodity * currency;
gnc_commodity_table *table;
const char *code;
table = gnc_get_current_commodities ();
code = gnc_locale_default_iso_currency_code ();
currency = gnc_commodity_table_lookup (table, GNC_COMMODITY_NS_CURRENCY, code);
return (currency ? currency : NULL);
}
gnc_commodity *
gnc_locale_default_currency (void)
{
gnc_commodity * currency = gnc_locale_default_currency_nodefault ();
return (currency ? currency :
gnc_commodity_table_lookup (gnc_get_current_commodities (),
GNC_COMMODITY_NS_CURRENCY, "USD"));
}
gnc_commodity *
gnc_default_currency (void)
{
gnc_commodity *currency = NULL;
gchar *choice, *mnemonic;
if (user_default_currency)
return gnc_commodity_table_lookup(gnc_get_current_commodities(),
GNC_COMMODITY_NS_CURRENCY,
user_default_currency);
choice = gnc_gconf_get_string(GCONF_GENERAL, KEY_CURRENCY_CHOICE, NULL);
if (choice && strcmp(choice, "other") == 0) {
mnemonic = gnc_gconf_get_string(GCONF_GENERAL, KEY_CURRENCY_OTHER, NULL);
currency = gnc_commodity_table_lookup(gnc_get_current_commodities(),
GNC_COMMODITY_NS_CURRENCY, mnemonic);
DEBUG("mnemonic %s, result %p", mnemonic, currency);
g_free(mnemonic);
g_free(choice);
}
if (!currency)
currency = gnc_locale_default_currency ();
if (currency) {
mnemonic = user_default_currency;
user_default_currency = g_strdup(gnc_commodity_get_mnemonic(currency));
g_free(mnemonic);
}
return currency;
}
gnc_commodity *
gnc_default_report_currency (void)
{
gnc_commodity *currency = NULL;
gchar *choice, *mnemonic;
if (user_report_currency)
return gnc_commodity_table_lookup(gnc_get_current_commodities(),
GNC_COMMODITY_NS_CURRENCY,
user_report_currency);
choice = gnc_gconf_get_string(GCONF_GENERAL_REPORT,
KEY_CURRENCY_CHOICE, NULL);
if (choice && strcmp(choice, "other") == 0) {
mnemonic = gnc_gconf_get_string(GCONF_GENERAL_REPORT,
KEY_CURRENCY_OTHER, NULL);
currency = gnc_commodity_table_lookup(gnc_get_current_commodities(),
GNC_COMMODITY_NS_CURRENCY, mnemonic);
DEBUG("mnemonic %s, result %p", mnemonic, currency);
g_free(choice);
g_free(mnemonic);
}
if (!currency)
currency = gnc_locale_default_currency ();
if (currency) {
mnemonic = user_report_currency;
user_report_currency = g_strdup(gnc_commodity_get_mnemonic(currency));
g_free(mnemonic);
}
return currency;
}
static void
gnc_currency_changed_cb (GConfEntry *entry, gpointer user_data)
{
user_default_currency = NULL;
user_report_currency = NULL;
gnc_hook_run(HOOK_CURRENCY_CHANGED, NULL);
}
/* Return the number of decimal places for this locale. */
int
gnc_locale_decimal_places (void)
{
static gboolean got_it = FALSE;
static int places;
struct lconv *lc;
if (got_it)
return places;
lc = gnc_localeconv();
places = lc->frac_digits;
/* frac_digits is already initialized by gnc_localeconv, hopefully
* to a reasonable default. */
got_it = TRUE;
return places;
}
static GList *locale_stack = NULL;
void
gnc_push_locale (const char *locale)
{
char *saved_locale;
g_return_if_fail (locale != NULL);
saved_locale = g_strdup (setlocale (LC_ALL, NULL));
locale_stack = g_list_prepend (locale_stack, saved_locale);
setlocale (LC_ALL, locale);
}
void
gnc_pop_locale (void)
{
char *saved_locale;
GList *node;
g_return_if_fail (locale_stack != NULL);
node = locale_stack;
saved_locale = node->data;
setlocale (LC_ALL, saved_locale);
locale_stack = g_list_remove_link (locale_stack, node);
g_list_free_1 (node);
g_free (saved_locale);
}
GNCPrintAmountInfo
gnc_default_print_info (gboolean use_symbol)
{
static GNCPrintAmountInfo info;
static gboolean got_it = FALSE;
struct lconv *lc;
/* These must be updated each time. */
info.use_symbol = use_symbol ? 1 : 0;
info.commodity = gnc_default_currency ();
if (got_it)
return info;
lc = gnc_localeconv ();
info.max_decimal_places = lc->frac_digits;
info.min_decimal_places = lc->frac_digits;
info.use_separators = 1;
info.use_locale = 1;
info.monetary = 1;
info.force_fit = 0;
info.round = 0;
got_it = TRUE;
return info;
}
static gboolean
is_decimal_fraction (int fraction, guint8 *max_decimal_places_p)
{
guint8 max_decimal_places = 0;
if (fraction <= 0)
return FALSE;
while (fraction != 1)
{
if (fraction % 10 != 0)
return FALSE;
fraction = fraction / 10;
max_decimal_places += 1;
}
if (max_decimal_places_p)
*max_decimal_places_p = max_decimal_places;
return TRUE;
}
GNCPrintAmountInfo
gnc_commodity_print_info (const gnc_commodity *commodity,
gboolean use_symbol)
{
GNCPrintAmountInfo info;
gboolean is_iso;
if (commodity == NULL)
return gnc_default_print_info (use_symbol);
info.commodity = commodity;
is_iso = gnc_commodity_is_iso (commodity);
if (is_decimal_fraction (gnc_commodity_get_fraction (commodity),
&info.max_decimal_places))
{
if (is_iso)
info.min_decimal_places = info.max_decimal_places;
else
info.min_decimal_places = 0;
}
else
info.max_decimal_places = info.min_decimal_places = 0;
info.use_separators = 1;
info.use_symbol = use_symbol ? 1 : 0;
info.use_locale = is_iso ? 1 : 0;
info.monetary = 1;
info.force_fit = 0;
info.round = 0;
return info;
}
static GNCPrintAmountInfo
gnc_account_print_info_helper(const Account *account, gboolean use_symbol,
gnc_commodity * (*efffunc)(const Account *),
int (*scufunc)(const Account*))
{
GNCPrintAmountInfo info;
gboolean is_iso;
int scu;
if (account == NULL)
return gnc_default_print_info (use_symbol);
info.commodity = efffunc (account);
is_iso = gnc_commodity_is_iso (info.commodity);
scu = scufunc (account);
if (is_decimal_fraction (scu, &info.max_decimal_places))
{
if (is_iso)
info.min_decimal_places = info.max_decimal_places;
else
info.min_decimal_places = 0;
}
else
info.max_decimal_places = info.min_decimal_places = 0;
info.use_separators = 1;
info.use_symbol = use_symbol ? 1 : 0;
info.use_locale = is_iso ? 1 : 0;
info.monetary = 1;
info.force_fit = 0;
info.round = 0;
return info;
}
GNCPrintAmountInfo
gnc_account_print_info (const Account *account, gboolean use_symbol)
{
return gnc_account_print_info_helper(account, use_symbol,
xaccAccountGetCommodity,
xaccAccountGetCommoditySCU);
}
GNCPrintAmountInfo
gnc_split_amount_print_info (Split *split, gboolean use_symbol)
{
if (!split)
{
GNCPrintAmountInfo info = gnc_default_share_print_info ();
info.use_symbol = use_symbol;
return info;
}
return gnc_account_print_info (xaccSplitGetAccount (split), use_symbol);
}
GNCPrintAmountInfo
gnc_split_value_print_info (Split *split, gboolean use_symbol)
{
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
Transaction *trans;
if (!split) return gnc_default_print_info (use_symbol);
2001-07-11 Dave Peticolas <dave@krondo.com> * src/test/test-xml-transaction.c: CIT (currency-in-transaction) * src/test/test-xml-account.c: CIT * src/test/gnc-test-stuff.c: CIT * src/scm/report/taxtxf.scm: CIT * src/scm/qif-import/qif-to-gnc.scm: CIT * src/scm/qif-import/qif-dialog-utils.scm: CIT * src/scm/text-export.scm: CIT * src/scm/price-quotes.scm: CIT * src/scm/main-window.scm: CIT * src/scm/engine-interface.scm: CIT * src/guile/gnc.gwp: CIT. remove unused api * src/gnome/glade/transfer.glade: remove currency account list * src/gnome/glade/account.glade: remove security gui * src/gnome/window-reconcile.c: CIT * src/gnome/window-main-summarybar.c: CIT * src/gnome/window-acct-tree.c: CIT * src/gnome/druid-stock-split.c: CIT * src/gnome/druid-qif-import.c: CIT * src/gnome/druid-hierarchy.c: CIT * src/gnome/druid-commodity.c: CIT * src/gnome/dialog-transfer.c: CIT. don't use currency accounts * src/gnome/dialog-commodities.c: CIT * src/gnome/dialog-account.c: CIT * src/gnome/account-tree.c: CIT * src/engine/sql/txn.c: fix for CIT * src/engine/sql/account.c: remove old currency/security cruft * src/engine/sixtp-dom-parsers.[ch]: allow parsing kvp data into an existing frame * src/engine/kvp_doc.txt: document new keys used for storing old currency/security fields * src/engine/io-gncxml-v2.c: CIT * src/engine/io-gncxml-r.c: CIT * src/engine/io-gncbin-r.c: CIT * src/engine/io-example-account.c: CIT * src/engine/gnc-transaction-xml-v2.c: CIT * src/engine/gnc-schedxaction-xml-v2.c: load kvp data into existing frame * src/engine/gnc-commodity.[ch]: add new lookup api, fixes for NULL pointers * src/engine/gnc-book.c: don't scrub (moved elsewhere) * src/engine/gnc-account-xml-v2.c: store transaction currency and account commodity. * src/engine/Transaction.[ch]: add a transaction currency * src/engine/TransLog.c: CIT * src/engine/Scrub.[ch]: add api for fixing up account and transaction commodities. * src/engine/SchedXaction.c: CIT * src/engine/Ledger-xml-parser-v1.c: CIT * src/engine/Group.[ch]: don't cache a group balance * src/engine/Account.[ch]: CIT. remove currency, security fields, add commodity fields. Store old currency/security in kvp_data. * src/engine/Account-xml-parser-v1.c: fix for CIT * src/gnc-ui-util.[ch]: fix for CIT * src/gnc-component-manager.c: remove deleted api * src/SplitLedger.c: fix for currency-in-transaction (CIT) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4943 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-07-12 00:13:21 +00:00
trans = xaccSplitGetParent (split);
return gnc_commodity_print_info (xaccTransGetCurrency (trans), use_symbol);
}
static GNCPrintAmountInfo
gnc_default_print_info_helper (int decplaces)
{
GNCPrintAmountInfo info;
info.commodity = NULL;
info.max_decimal_places = decplaces;
info.min_decimal_places = 0;
info.use_separators = 1;
info.use_symbol = 0;
info.use_locale = 1;
info.monetary = 1;
info.force_fit = 0;
info.round = 0;
return info;
}
GNCPrintAmountInfo
gnc_default_share_print_info (void)
{
static GNCPrintAmountInfo info;
static gboolean got_it = FALSE;
if (!got_it)
{
info = gnc_default_print_info_helper (5);
got_it = TRUE;
}
return info;
}
GNCPrintAmountInfo
gnc_share_print_info_places (int decplaces)
{
GNCPrintAmountInfo info;
info = gnc_default_share_print_info ();
info.max_decimal_places = decplaces;
info.min_decimal_places = decplaces;
info.force_fit = 1;
info.round = 1;
return info;
}
GNCPrintAmountInfo
gnc_default_price_print_info (void)
{
static GNCPrintAmountInfo info;
static gboolean got_it = FALSE;
if (!got_it)
{
info = gnc_default_print_info_helper (6);
got_it = TRUE;
}
return info;
}
GNCPrintAmountInfo
gnc_integral_print_info (void)
{
static GNCPrintAmountInfo info;
static gboolean got_it = FALSE;
if (!got_it)
{
info = gnc_default_print_info_helper (0);
got_it = TRUE;
}
return info;
}
/* Utility function for printing non-negative amounts */
static int
PrintAmountInternal(char *buf, gnc_numeric val, const GNCPrintAmountInfo *info)
{
struct lconv *lc = gnc_localeconv();
int num_whole_digits;
char temp_buf[128];
gnc_numeric whole, rounding;
int min_dp, max_dp;
2001-10-11 Dave Peticolas <dave@krondo.com> * src/register/register-core/table-allgui.c (gnc_table_traverse_update): remove assert * src/import-export/binary-import/druid-commodity.c: use g_assert * src/guile/gnucash.c.in (gnc_main): use g_assert * src/gnome-utils/gnc-account-tree.c: remove asserts * src/gnome/dialog-fincalc.c (normalize_period): remove asserts * src/gnome/dialog-find-transactions.c: remove asserts * src/gnome/dialog-scheduledxaction.c: remove asserts * src/gnome/reconcile-list.c: remove asserts * src/gnome/window-register.c: remove asserts * src/engine/Account.c: remove asserts * src/engine/Group.c: remove asserts * src/engine/Query.c:remove asserts * src/engine/TransLog.c: remove asserts * src/engine/Transaction.c: remove asserts * src/engine/gnc-engine-util.h: don't include assert.h * src/engine/gnc-engine.c: remove asserts * src/engine/gnc-numeric.c: remove asserts * src/engine/gnc-pricedb.c (gnc_price_unref): remove asserts * src/engine/messages.c: remove asserts * src/backend/file/test/test-xml-account.c: fix text handling * src/backend/file/test/test-xml-commodity.c: fix text handling * src/backend/file/test/test-xml-transaction.c: fix text handling * src/backend/file/gnc-freqspec-xml-v2.c: fix text handling * src/backend/file/gnc-pricedb-xml-v2.c: fix text handling * src/backend/file/gnc-schedxaction-xml-v2.c: fix text handling * src/backend/file/gnc-transaction-xml-v2.c: fix text handling * src/backend/file/io-gncxml-v1.c: fix text handling * src/backend/file/sixtp-dom-parsers.c: fix text handling * src/backend/file/sixtp-to-dom-parser.c (dom_chars_handler): don't ignore whitespace. This fixes text handling for strings like "< <" * src/app-utils/gnc-ui-util.c: remove asserts * src/app-utils/option-util.c: remove asserts git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@5576 57a11ea4-9604-0410-9ed3-97b8803252fd
2001-10-11 11:35:50 +00:00
g_return_val_if_fail (info != NULL, 0);
if (gnc_numeric_check (val))
{
PWARN ("Bad numeric.");
*buf = '\0';
return 0;
}
/* print the absolute value */
val = gnc_numeric_abs (val);
/* Force at least auto_decimal_places zeros */
if (auto_decimal_enabled) {
min_dp = MAX(auto_decimal_places, info->min_decimal_places);
max_dp = MAX(auto_decimal_places, info->max_decimal_places);
} else {
min_dp = info->min_decimal_places;
max_dp = info->max_decimal_places;
}
/* Don to limit the number of decimal places _UNLESS_ force_fit is
* true. */
if (!info->force_fit)
max_dp = 99;
/* rounding? -- can only ROUND if force_fit is also true */
if (info->round && info->force_fit) {
rounding.num = 5; /* Limit the denom to 10^13 ~= 2^44, leaving max at ~524288 */
rounding.denom = pow(10, max_dp + 1);
val = gnc_numeric_add(val, rounding, GNC_DENOM_AUTO, GNC_DENOM_LCD);
/* Yes, rounding up can cause overflow. Check for it. */
if (gnc_numeric_check(val)) {
PWARN("Bad numeric from rounding.");
*buf = '\0';
return 0;
}
}
/* calculate the integer part and the remainder */
whole = gnc_numeric_convert(val, 1, GNC_HOW_RND_TRUNC);
val = gnc_numeric_sub (val, whole, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
if (gnc_numeric_check (val))
{
PWARN ("Problem with remainder.");
*buf = '\0';
return 0;
}
/* print the integer part without separators */
sprintf(temp_buf, "%" G_GINT64_FORMAT, whole.num);
num_whole_digits = strlen (temp_buf);
if (!info->use_separators)
strcpy (buf, temp_buf);
else
{
int group_count;
char *separator;
char *temp_ptr;
char *buf_ptr;
char *group;
gchar *rev_buf;
if (info->monetary)
{
separator = lc->mon_thousands_sep;
group = lc->mon_grouping;
}
else
{
separator = lc->thousands_sep;
group = lc->grouping;
}
buf_ptr = buf;
temp_ptr = &temp_buf[num_whole_digits - 1];
group_count = 0;
while (temp_ptr != temp_buf)
{
*buf_ptr++ = *temp_ptr--;
if (*group != CHAR_MAX)
{
group_count++;
if (group_count == *group)
{
g_utf8_strncpy(buf_ptr, separator, 1);
buf_ptr = g_utf8_find_next_char(buf_ptr, NULL);
group_count = 0;
/* Peek ahead at the next group code */
switch (group[1])
{
/* A null char means repeat the last group indefinitely */
case '\0':
break;
/* CHAR_MAX means no more grouping allowed */
case CHAR_MAX:
/* fall through */
/* Anything else means another group size */
default:
group++;
break;
}
}
}
}
/* We built the string backwards, now reverse */
*buf_ptr++ = *temp_ptr;
*buf_ptr = '\0';
rev_buf = g_utf8_strreverse(buf, -1);
strcpy (buf, rev_buf);
g_free(rev_buf);
} /* endif */
/* at this point, buf contains the whole part of the number */
/* If it's not decimal, print the fraction as an expression */
if (!is_decimal_fraction (val.denom, NULL))
{
if (!gnc_numeric_zero_p (val))
{
val = gnc_numeric_reduce (val);
if (val.denom > 0)
sprintf (temp_buf, " + %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT,
val.num, val.denom);
else
sprintf (temp_buf, " + %" G_GINT64_FORMAT " * %" G_GINT64_FORMAT,
val.num, -val.denom);
strcat (buf, temp_buf);
}
}
else
{
char *decimal_point;
guint8 num_decimal_places = 0;
char *temp_ptr = temp_buf;
decimal_point = info->monetary ?
lc->mon_decimal_point : lc->decimal_point;
g_utf8_strncpy(temp_ptr, decimal_point, 1);
temp_ptr = g_utf8_find_next_char(temp_ptr, NULL);
while (!gnc_numeric_zero_p (val) && (val.denom != 1) &&
(num_decimal_places < max_dp))
{
gint64 digit;
val.denom = val.denom / 10;
digit = val.num / val.denom;
*temp_ptr++ = digit + '0';
num_decimal_places++;
val.num = val.num - (digit * val.denom);
}
while (num_decimal_places < min_dp)
{
*temp_ptr++ = '0';
num_decimal_places++;
}
/* cap the end and move to the last character */
*temp_ptr-- = '\0';
/* Here we strip off trailing decimal zeros per the argument. */
while (*temp_ptr == '0' && num_decimal_places > min_dp)
{
*temp_ptr-- = '\0';
num_decimal_places--;
}
if (num_decimal_places > max_dp)
{
PWARN ("max_decimal_places too small; limit %d, value %s%s",
info->max_decimal_places, buf, temp_buf);
}
if (num_decimal_places > 0)
strcat (buf, temp_buf);
}
return strlen(buf);
}
/**
* @param bufp Should be at least 64 chars.
**/
int
xaccSPrintAmount (char * bufp, gnc_numeric val, GNCPrintAmountInfo info)
{
struct lconv *lc;
char *orig_bufp = bufp;
const char *currency_symbol;
const char *sign;
char cs_precedes;
char sep_by_space;
char sign_posn;
gboolean print_sign = TRUE;
gboolean is_shares = FALSE;
if (!bufp)
return 0;
lc = gnc_localeconv();
if (info.use_symbol)
{
/* There was a bug here: don't use gnc_locale_default_currency */
if (gnc_commodity_equiv (info.commodity,
gnc_locale_default_currency_nodefault ()))
{
currency_symbol = lc->currency_symbol;
}
else
{
if (info.commodity && !gnc_commodity_is_iso (info.commodity))
is_shares = TRUE;
currency_symbol = gnc_commodity_get_mnemonic (info.commodity);
info.use_locale = 0;
}
if (currency_symbol == NULL)
currency_symbol = "";
}
else
currency_symbol = NULL;
if (!info.use_locale)
{
cs_precedes = is_shares ? 0 : 1;
sep_by_space = 1;
}
else
{
if (gnc_numeric_negative_p (val))
{
cs_precedes = lc->n_cs_precedes;
sep_by_space = lc->n_sep_by_space;
}
else
{
cs_precedes = lc->p_cs_precedes;
sep_by_space = lc->p_sep_by_space;
}
}
if (gnc_numeric_negative_p (val))
{
sign = lc->negative_sign;
sign_posn = lc->n_sign_posn;
}
else
{
sign = lc->positive_sign;
sign_posn = lc->p_sign_posn;
}
if (gnc_numeric_zero_p (val) || (sign == NULL) || (sign[0] == 0))
print_sign = FALSE;
/* See if we print sign now */
if (print_sign && (sign_posn == 1))
bufp = g_stpcpy(bufp, sign);
/* Now see if we print currency */
if (cs_precedes)
{
/* See if we print sign now */
if (print_sign && (sign_posn == 3))
bufp = g_stpcpy(bufp, sign);
if (info.use_symbol)
{
bufp = g_stpcpy(bufp, currency_symbol);
if (sep_by_space)
bufp = g_stpcpy(bufp, " ");
}
/* See if we print sign now */
if (print_sign && (sign_posn == 4))
bufp = g_stpcpy(bufp, sign);
}
/* Now see if we print parentheses */
if (print_sign && (sign_posn == 0))
bufp = g_stpcpy(bufp, "(");
/* Now print the value */
bufp += PrintAmountInternal(bufp, val, &info);
/* Now see if we print parentheses */
if (print_sign && (sign_posn == 0))
bufp = g_stpcpy(bufp, ")");
/* Now see if we print currency */
if (!cs_precedes)
{
/* See if we print sign now */
if (print_sign && (sign_posn == 3))
bufp = g_stpcpy(bufp, sign);
if (info.use_symbol)
{
if (sep_by_space)
bufp = g_stpcpy(bufp, " ");
bufp = g_stpcpy(bufp, currency_symbol);
}
/* See if we print sign now */
if (print_sign && (sign_posn == 4))
bufp = g_stpcpy(bufp, sign);
}
/* See if we print sign now */
if (print_sign && (sign_posn == 2))
bufp = g_stpcpy(bufp, sign);
/* return length of printed string */
return (bufp - orig_bufp);
}
const char *
xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info)
{
/* hack alert -- this is not thread safe ... */
static char buf[1024];
if (!xaccSPrintAmount (buf, val, info))
buf[0] = '\0';
/* its OK to return buf, since we declared it static */
return buf;
}
/********************************************************************\
* xaccParseAmount *
* parses amount strings using locale data *
* *
* Args: in_str -- pointer to string rep of num *
* monetary -- boolean indicating whether value is monetary *
* result -- pointer to result location, may be NULL *
* endstr -- used to store first digit not used in parsing *
* Return: gboolean -- TRUE if a number found and parsed *
* If FALSE, result is not changed *
\********************************************************************/
/* Parsing state machine states */
typedef enum
{
START_ST, /* Parsing initial whitespace */
NEG_ST, /* Parsed a negative sign or a left paren */
PRE_GROUP_ST, /* Parsing digits before grouping and decimal characters */
START_GROUP_ST, /* Start of a digit group encountered (possibly) */
IN_GROUP_ST, /* Within a digit group */
FRAC_ST, /* Parsing the fractional portion of a number */
DONE_ST, /* Finished, number is correct module grouping constraints */
NO_NUM_ST /* Finished, number was malformed */
} ParseState;
#define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST))
G_INLINE_FUNC long long int multiplier (int num_decimals);
long long int
multiplier (int num_decimals)
{
switch (num_decimals)
{
case 8:
return 100000000;
case 7:
return 10000000;
case 6:
return 1000000;
case 5:
return 100000;
case 4:
return 10000;
case 3:
return 1000;
case 2:
return 100;
case 1:
return 10;
default:
PERR("bad fraction length");
g_assert_not_reached();
break;
}
return 1;
}
gboolean
xaccParseAmount (const char * in_str, gboolean monetary, gnc_numeric *result,
char **endstr)
{
struct lconv *lc = gnc_localeconv();
gunichar negative_sign;
gunichar decimal_point;
gunichar group_separator;
char *group;
negative_sign = g_utf8_get_char(lc->negative_sign);
if (monetary)
{
group_separator = g_utf8_get_char(lc->mon_thousands_sep);
decimal_point = g_utf8_get_char(lc->mon_decimal_point);
group = lc->mon_grouping;
}
else
{
group_separator = g_utf8_get_char(lc->thousands_sep);
decimal_point = g_utf8_get_char(lc->decimal_point);
group = lc->grouping;
}
return xaccParseAmountExtended(in_str, monetary, negative_sign, decimal_point,
group_separator, group, NULL, result, endstr);
}
/* Note: xaccParseAmountExtended causes test-print-parse-amount
to fail if QOF_SCANF_LLD is simply replaced by G_GINT64_FORMAT. Why?
A: Because scanf and printf use different symbols for 64-bit numbers.
*/
gboolean
xaccParseAmountExtended (const char * in_str, gboolean monetary,
gunichar negative_sign, gunichar decimal_point,
gunichar group_separator, char *group, char *ignore_list,
gnc_numeric *result, char **endstr)
{
gboolean is_negative;
gboolean got_decimal;
gboolean need_paren;
GList * group_data;
long long int numer;
long long int denom;
int count, group_count;
ParseState state;
const gchar *in;
gunichar uc;
gchar *out_str;
gchar *out;
/* Initialize *endstr to in_str */
if (endstr != NULL)
*endstr = (char *) in_str;
if (in_str == NULL)
return FALSE;
if (!g_utf8_validate(in_str, -1, &in)) {
printf("Invalid utf8 string '%s'. Bad character at position %ld.\n",
in_str, g_utf8_pointer_to_offset (in_str, in));
return FALSE;
}
/* 'out_str' will be used to store digits for numeric conversion.
* 'out' will be used to traverse out_str. */
out = out_str = g_new(gchar, strlen(in_str) + 128);
/* 'in' is used to traverse 'in_str'. */
in = in_str;
is_negative = FALSE;
got_decimal = FALSE;
need_paren = FALSE;
group_data = NULL;
group_count = 0;
numer = 0;
denom = 1;
/* Initialize the state machine */
state = START_ST;
/* This while loop implements a state machine for parsing numbers. */
while (TRUE)
{
ParseState next_state = state;
uc = g_utf8_get_char(in);
/* Ignore anything in the 'ignore list' */
if (ignore_list && uc && g_utf8_strchr(ignore_list, -1, uc) != NULL) {
in = g_utf8_next_char(in);
continue;
}
/* Note we never need to check for the end of 'in_str' explicitly.
* The 'else' clauses on all the state transitions will handle that. */
switch (state)
{
/* START_ST means we have parsed 0 or more whitespace characters */
case START_ST:
if (g_unichar_isdigit(uc))
{
count = g_unichar_to_utf8(uc, out);
out += count; /* we record the digits themselves in out_str
* for later conversion by libc routines */
next_state = PRE_GROUP_ST;
}
else if (uc == decimal_point)
{
next_state = FRAC_ST;
}
else if (g_unichar_isspace(uc))
{
}
else if (uc == negative_sign)
{
is_negative = TRUE;
next_state = NEG_ST;
}
else if (uc == '(')
{
is_negative = TRUE;
need_paren = TRUE;
next_state = NEG_ST;
}
else
{
next_state = NO_NUM_ST;
}
break;
/* NEG_ST means we have just parsed a negative sign. For now,
* we only recognize formats where the negative sign comes first. */
case NEG_ST:
if (g_unichar_isdigit(uc))
{
count = g_unichar_to_utf8(uc, out);
out += count;
next_state = PRE_GROUP_ST;
}
else if (uc == decimal_point)
{
next_state = FRAC_ST;
}
else if (g_unichar_isspace(uc))
{
}
else
{
next_state = NO_NUM_ST;
}
break;
/* PRE_GROUP_ST means we have started parsing the number, but
* have not encountered a decimal point or a grouping character. */
case PRE_GROUP_ST:
if (g_unichar_isdigit(uc))
{
count = g_unichar_to_utf8(uc, out);
out += count;
}
else if (uc == decimal_point)
{
next_state = FRAC_ST;
}
else if (uc == group_separator)
{
next_state = START_GROUP_ST;
}
else if (uc == ')' && need_paren)
{
next_state = DONE_ST;
need_paren = FALSE;
}
else
{
next_state = DONE_ST;
}
break;
/* START_GROUP_ST means we have just parsed a group character.
* Note that group characters might be whitespace!!! In general,
* if a decimal point or a group character is whitespace, we
* try to interpret it in the fashion that will allow parsing
* of the current number to continue. */
case START_GROUP_ST:
if (g_unichar_isdigit(uc))
{
count = g_unichar_to_utf8(uc, out);
out += count;
group_count++; /* We record the number of digits
* in the group for later checking. */
next_state = IN_GROUP_ST;
}
else if (uc == decimal_point)
{
/* If we now get a decimal point, and both the decimal
* and the group separator are also whitespace, assume
* the last group separator was actually whitespace and
* stop parsing. Otherwise, there's a problem. */
if (g_unichar_isspace(group_separator) &&
g_unichar_isspace(decimal_point))
next_state = DONE_ST;
else
next_state = NO_NUM_ST;
}
else if (uc == ')' && need_paren)
{
if (g_unichar_isspace(group_separator))
{
next_state = DONE_ST;
need_paren = FALSE;
}
else
next_state = NO_NUM_ST;
}
else
{
/* If the last group separator is also whitespace,
* assume it was intended as such and stop parsing.
* Otherwise, there is a problem. */
if (g_unichar_isspace(group_separator))
next_state = DONE_ST;
else
next_state = NO_NUM_ST;
}
break;
/* IN_GROUP_ST means we are in the middle of parsing
* a group of digits. */
case IN_GROUP_ST:
if (g_unichar_isdigit(uc))
{
count = g_unichar_to_utf8(uc, out);
out += count;
group_count++; /* We record the number of digits
* in the group for later checking. */
}
else if (uc == decimal_point)
{
next_state = FRAC_ST;
}
else if (uc == group_separator)
{
next_state = START_GROUP_ST;
}
else if (uc == ')' && need_paren)
{
next_state = DONE_ST;
need_paren = FALSE;
}
else
{
next_state = DONE_ST;
}
break;
/* FRAC_ST means we are now parsing fractional digits. */
case FRAC_ST:
if (g_unichar_isdigit(uc))
{
count = g_unichar_to_utf8(uc, out);
out += count;
}
else if (uc == decimal_point)
{
/* If a subsequent decimal point is also whitespace,
* assume it was intended as such and stop parsing.
* Otherwise, there is a problem. */
if (g_unichar_isspace(decimal_point))
next_state = DONE_ST;
else
next_state = NO_NUM_ST;
}
else if (uc == group_separator)
{
/* If a subsequent group separator is also whitespace,
* assume it was intended as such and stop parsing.
* Otherwise, there is a problem. */
if (g_unichar_isspace(group_separator))
next_state = DONE_ST;
else
next_state = NO_NUM_ST;
}
else if (uc == ')' && need_paren)
{
next_state = DONE_ST;
need_paren = FALSE;
}
else
{
next_state = DONE_ST;
}
break;
default:
PERR("bad state");
g_assert_not_reached();
break;
}
/* If we're moving out of the IN_GROUP_ST, record data for the group */
if ((state == IN_GROUP_ST) && (next_state != IN_GROUP_ST))
{
group_data = g_list_prepend(group_data, GINT_TO_POINTER(group_count));
group_count = 0;
}
/* If we're moving into the FRAC_ST or out of the machine
* without going through FRAC_ST, record the integral value. */
if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
((next_state == DONE_ST) && !got_decimal))
{
*out = '\0';
if (*out_str != '\0' && sscanf(out_str, QOF_SCANF_LLD, &numer) < 1)
{
next_state = NO_NUM_ST;
}
else if (next_state == FRAC_ST)
{
/* reset the out pointer to record the fraction */
out = out_str;
*out = '\0';
got_decimal = TRUE;
}
}
state = next_state;
if (done_state (state))
break;
in = g_utf8_next_char(in);
}
/* If there was an error, just quit */
if (need_paren || (state == NO_NUM_ST))
{
g_free(out_str);
g_list_free(group_data);
return FALSE;
}
/* If there were groups, validate them */
if (group_data != NULL)
{
gboolean good_grouping = TRUE;
GList *node;
/* The groups were built in reverse order. This
* is the easiest order to verify them in. */
for (node = group_data; group && node; node = node->next)
{
/* Verify group size */
if (*group != GPOINTER_TO_INT(node->data))
{
good_grouping = FALSE;
break;
}
/* Peek ahead at the next group code */
switch (group[1])
{
/* A null char means repeat the last group indefinitely */
case '\0':
break;
/* CHAR_MAX means no more grouping allowed */
case CHAR_MAX:
if (node->next != NULL)
good_grouping = FALSE;
break;
/* Anything else means another group size */
default:
group++;
break;
}
if (!good_grouping)
break;
}
g_list_free(group_data);
if (!good_grouping)
{
g_free(out_str);
return FALSE;
}
}
/* Cap the end of the fraction string, if any */
*out = '\0';
/* Add in fractional value */
if (got_decimal && (*out_str != '\0'))
{
size_t len;
long long int fraction;
len = strlen(out_str);
if (len > 8)
{
out_str[8] = '\0';
len = 8;
}
if (sscanf (out_str, QOF_SCANF_LLD, &fraction) < 1)
{
g_free(out_str);
return FALSE;
}
denom = multiplier(len);
numer *= denom;
numer += fraction;
}
else if (monetary && auto_decimal_enabled && !got_decimal)
{
if ((auto_decimal_places > 0) && (auto_decimal_places < 9))
{
denom = multiplier(auto_decimal_places);
/* No need to multiply numer by denom at this point,
* since by specifying the auto decimal places the
* user has effectively determined the scaling factor
* for the numerator they entered.
*/
}
}
if (result != NULL)
{
*result = gnc_numeric_create (numer, denom);
if (is_negative)
*result = gnc_numeric_neg (*result);
}
if (endstr != NULL)
*endstr = (char *) in;
g_free (out_str);
return TRUE;
}
/* enable/disable the auto_decimal_enabled option */
static void
gnc_set_auto_decimal_enabled (GConfEntry *entry, gpointer user_data)
{
GConfValue *value;
value = gconf_entry_get_value(entry);
auto_decimal_enabled = gconf_value_get_bool(value);
}
/* set the number of auto decimal places to use */
static void
gnc_set_auto_decimal_places (GConfEntry *entry, gpointer user_data)
{
GConfValue *value;
value = gconf_entry_get_value(entry);
auto_decimal_places = gconf_value_get_float(value);
}
static void
gnc_auto_decimal_init (void)
{
auto_decimal_enabled =
gnc_gconf_get_bool(GCONF_GENERAL, "auto_decimal_point", NULL);
auto_decimal_places =
gnc_gconf_get_float(GCONF_GENERAL, "auto_decimal_places", NULL);
}
void
gnc_ui_util_init (void)
{
gnc_configure_account_separator ();
gnc_auto_decimal_init();
gnc_gconf_general_register_cb(KEY_ACCOUNT_SEPARATOR,
(GncGconfGeneralCb)gnc_configure_account_separator,
NULL);
gnc_gconf_general_register_cb(KEY_REVERSED_ACCOUNTS,
(GncGconfGeneralCb)gnc_configure_reverse_balance,
NULL);
gnc_gconf_general_register_cb(KEY_CURRENCY_CHOICE,
gnc_currency_changed_cb, NULL);
gnc_gconf_general_register_cb(KEY_CURRENCY_OTHER,
gnc_currency_changed_cb, NULL);
gnc_gconf_general_register_cb("auto_decimal_point",
gnc_set_auto_decimal_enabled,
NULL);
gnc_gconf_general_register_cb("auto_decimal_places",
gnc_set_auto_decimal_places,
NULL);
}
/* These implementations are rather lame. */
#ifndef HAVE_TOWUPPER
gint32
towupper (gint32 wc)
{
if (wc > 127)
return wc;
return toupper ((int) wc);
}
int
iswlower (gint32 wc)
{
if (wc > 127)
return 1;
return islower ((int) wc);
}
#endif