|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * gnc-ui-util.c -- utility functions for the GnuCash UI * 00003 * Copyright (C) 2000 Dave Peticolas <dave@krondo.com> * 00004 * * 00005 * This program is free software; you can redistribute it and/or * 00006 * modify it under the terms of the GNU General Public License as * 00007 * published by the Free Software Foundation; either version 2 of * 00008 * the License, or (at your option) any later version. * 00009 * * 00010 * This program is distributed in the hope that it will be useful, * 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00013 * GNU General Public License for more details. * 00014 * * 00015 * You should have received a copy of the GNU General Public License* 00016 * along with this program; if not, contact: * 00017 * * 00018 * Free Software Foundation Voice: +1-617-542-5942 * 00019 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00020 * Boston, MA 02110-1301, USA gnu@gnu.org * 00021 \********************************************************************/ 00022 00023 #include "config.h" 00024 00025 #include "gnc-ui-util.h" 00026 00027 #include <glib.h> 00028 #include <glib/gi18n.h> 00029 #include <libguile.h> 00030 #include <ctype.h> 00031 #include <errno.h> 00032 #include <limits.h> 00033 #include <locale.h> 00034 #include <math.h> 00035 #if defined(G_OS_WIN32) && !defined(_MSC_VER) 00036 # include <pow.h> 00037 #endif 00038 #include <stdarg.h> 00039 #include <stdlib.h> 00040 #include <stdio.h> 00041 #include <string.h> 00042 00043 00044 #include "qof.h" 00045 #include "guile-mappings.h" 00046 #include "core-utils/gnc-gconf-utils.h" 00047 #include "gnc-module/gnc-module.h" 00048 #include "engine/Account.h" 00049 #include "engine/Transaction.h" 00050 #include "engine/gnc-engine.h" 00051 #include "app-utils/gnc-euro.h" 00052 #include "engine/gnc-hooks.h" 00053 #include "engine/gnc-session.h" 00054 #include "gnc-locale-utils.h" 00055 00056 #define KEY_CURRENCY_CHOICE "currency_choice" 00057 #define KEY_CURRENCY_OTHER "currency_other" 00058 #define KEY_REVERSED_ACCOUNTS "reversed_accounts" 00059 00060 static QofLogModule log_module = GNC_MOD_GUI; 00061 00062 static gboolean auto_decimal_enabled = FALSE; 00063 static int auto_decimal_places = 2; /* default, can be changed */ 00064 00065 static gboolean reverse_balance_inited = FALSE; 00066 static gboolean reverse_type[NUM_ACCOUNT_TYPES]; 00067 00068 /* Cache currency ISO codes and only look them up in gconf when 00069 * absolutely necessary. Can't cache a pointer to the data structure 00070 * as that will change any time the book changes. */ 00071 static gchar *user_default_currency = NULL; 00072 static gchar *user_report_currency = NULL; 00073 00074 /********************************************************************\ 00075 * gnc_configure_account_separator * 00076 * updates the current account separator character * 00077 * * 00078 * Args: none * 00079 \*******************************************************************/ 00080 static void 00081 gnc_configure_account_separator (void) 00082 { 00083 const gchar *separator; 00084 char *string; 00085 00086 string = gnc_gconf_get_string(GCONF_GENERAL, KEY_ACCOUNT_SEPARATOR, NULL); 00087 00088 if (!string || !*string || safe_strcmp(string, "colon") == 0) 00089 separator = ":"; 00090 else if (safe_strcmp(string, "slash") == 0) 00091 separator = "/"; 00092 else if (safe_strcmp(string, "backslash") == 0) 00093 separator = "\\"; 00094 else if (safe_strcmp(string, "dash") == 0) 00095 separator = "-"; 00096 else if (safe_strcmp(string, "period") == 0) 00097 separator = "."; 00098 else 00099 separator = string; 00100 00101 gnc_set_account_separator(separator); 00102 00103 if (string != NULL) 00104 free(string); 00105 } 00106 00107 00108 static void 00109 gnc_configure_reverse_balance (void) 00110 { 00111 gchar *choice; 00112 gint i; 00113 00114 for (i = 0; i < NUM_ACCOUNT_TYPES; i++) 00115 reverse_type[i] = FALSE; 00116 00117 choice = gnc_gconf_get_string(GCONF_GENERAL, KEY_REVERSED_ACCOUNTS, NULL); 00118 00119 if (safe_strcmp (choice, "none") == 0) 00120 { 00121 } 00122 else if (safe_strcmp (choice, "income_expense") == 0) 00123 { 00124 reverse_type[ACCT_TYPE_INCOME] = TRUE; 00125 reverse_type[ACCT_TYPE_EXPENSE] = TRUE; 00126 } 00127 else 00128 { 00129 if (safe_strcmp (choice, "credit") != 0) 00130 PERR("bad value '%s'", choice ? choice : "(null)"); 00131 reverse_type[ACCT_TYPE_LIABILITY] = TRUE; 00132 reverse_type[ACCT_TYPE_PAYABLE] = TRUE; 00133 reverse_type[ACCT_TYPE_EQUITY] = TRUE; 00134 reverse_type[ACCT_TYPE_INCOME] = TRUE; 00135 reverse_type[ACCT_TYPE_CREDIT] = TRUE; 00136 } 00137 00138 if (choice != NULL) 00139 free (choice); 00140 } 00141 00142 static void 00143 gnc_reverse_balance_init (void) 00144 { 00145 gnc_configure_reverse_balance (); 00146 reverse_balance_inited = TRUE; 00147 } 00148 00149 gboolean 00150 gnc_reverse_balance (const Account *account) 00151 { 00152 int type; 00153 00154 if (account == NULL) 00155 return FALSE; 00156 00157 type = xaccAccountGetType (account); 00158 if ((type < 0) || (type >= NUM_ACCOUNT_TYPES)) 00159 return FALSE; 00160 00161 if (!reverse_balance_inited) 00162 gnc_reverse_balance_init (); 00163 00164 return reverse_type[type]; 00165 } 00166 00167 00168 gchar * 00169 gnc_get_default_directory (const gchar *gconf_section) 00170 { 00171 gchar *dir; 00172 00173 dir = gnc_gconf_get_string (gconf_section, KEY_LAST_PATH, NULL); 00174 if (!dir) 00175 #ifdef G_OS_WIN32 00176 dir = g_strdup (g_get_user_data_dir ()); /* equivalent of "My Documents" */ 00177 #else 00178 dir = g_strdup (g_get_home_dir ()); 00179 #endif 00180 00181 return dir; 00182 } 00183 00184 void 00185 gnc_set_default_directory (const gchar *gconf_section, const gchar *directory) 00186 { 00187 gnc_gconf_set_string(gconf_section, KEY_LAST_PATH, directory, NULL); 00188 } 00189 00190 QofBook * 00191 gnc_get_current_book (void) 00192 { 00193 return qof_session_get_book (gnc_get_current_session ()); 00194 } 00195 00196 #define OPTION_TAXUS_NAME "book/tax_US/name" 00197 #define OPTION_TAXUS_TYPE "book/tax_US/type" 00198 00199 void 00200 gnc_set_current_book_tax_name (const gchar *tax_name) 00201 { 00202 qof_book_set_string_option(gnc_get_current_book(), OPTION_TAXUS_NAME, tax_name); 00203 } 00204 00205 const gchar * 00206 gnc_get_current_book_tax_name (void) 00207 { 00208 return qof_book_get_string_option(gnc_get_current_book(), OPTION_TAXUS_NAME); 00209 } 00210 00211 void 00212 gnc_set_current_book_tax_type (const gchar *tax_type) 00213 { 00214 qof_book_set_string_option(gnc_get_current_book(), OPTION_TAXUS_TYPE, tax_type); 00215 } 00216 00217 const gchar * 00218 gnc_get_current_book_tax_type (void) 00219 { 00220 return qof_book_get_string_option(gnc_get_current_book(), OPTION_TAXUS_TYPE); 00221 } 00222 00223 Account * 00224 gnc_get_current_root_account (void) 00225 { 00226 return gnc_book_get_root_account (gnc_get_current_book ()); 00227 } 00228 00229 gnc_commodity_table * 00230 gnc_get_current_commodities (void) 00231 { 00232 return gnc_commodity_table_get_table (gnc_get_current_book ()); 00233 } 00234 00235 gchar * 00236 gnc_get_account_name_for_register(const Account *account) 00237 { 00238 gboolean show_leaf_accounts; 00239 show_leaf_accounts = gnc_gconf_get_bool(GCONF_GENERAL_REGISTER, 00240 KEY_SHOW_LEAF_ACCOUNT_NAMES, NULL); 00241 00242 if (show_leaf_accounts) 00243 return g_strdup (xaccAccountGetName (account)); 00244 else 00245 return gnc_account_get_full_name (account); 00246 } 00247 00248 Account * 00249 gnc_account_lookup_for_register(const Account *base_account, const char *name) 00250 { 00251 gboolean show_leaf_accounts; 00252 show_leaf_accounts = gnc_gconf_get_bool(GCONF_GENERAL_REGISTER, 00253 KEY_SHOW_LEAF_ACCOUNT_NAMES, NULL); 00254 00255 if (show_leaf_accounts) 00256 return gnc_account_lookup_by_name (base_account, name); 00257 else 00258 return gnc_account_lookup_by_full_name (base_account, name); 00259 } 00260 00261 00262 /* Caller is responsible for g_free'ing returned memory */ 00263 char * 00264 gnc_ui_account_get_tax_info_string (const Account *account) 00265 { 00266 static SCM get_form = SCM_UNDEFINED; 00267 static SCM get_desc = SCM_UNDEFINED; 00268 00269 gboolean tax_related = FALSE; 00270 const char *code; 00271 00272 if (!account) 00273 return NULL; 00274 00275 tax_related = xaccAccountGetTaxRelated (account); 00276 code = xaccAccountGetTaxUSCode (account); 00277 00278 if (!code) 00279 { 00280 if (!tax_related) 00281 return NULL; 00282 /* tax_related && !code */ 00283 else 00284 /* Translators: This and the following strings appear on 00285 * the account tab if the Tax Info column is displayed, 00286 * i.e. if the user wants to record the tax form number 00287 * and location on that tax form which corresponds to this 00288 * gnucash account. For the US Income Tax support in 00289 * gnucash, each tax code that can be assigned to an 00290 * account generally corresponds to a specific line number 00291 * on a paper form and each form has a unique 00292 * identification (e.g., Form 1040, Schedule A). */ 00293 return g_strdup (_("Tax-related but has no tax code")); 00294 } 00295 else /* with tax code */ 00296 { 00297 const gchar *tax_type; 00298 GNCAccountType atype; 00299 SCM tax_entity_type; 00300 SCM category; 00301 gchar *num_code = NULL; 00302 const gchar *prefix = "N"; 00303 gchar *return_string = NULL; 00304 00305 tax_type = gnc_get_current_book_tax_type (); 00306 if (tax_type == NULL || (safe_strcmp (tax_type, "") == 0)) 00307 return g_strdup (_("Tax entity type not specified")); 00308 00309 atype = xaccAccountGetType (account); 00310 tax_entity_type = scm_from_locale_string (tax_type); 00311 00312 if (get_form == SCM_UNDEFINED) 00313 { 00314 GNCModule module; 00315 const gchar *tax_module; 00316 /* load the tax info */ 00317 #ifdef LOCALE_SPECIFIC_TAX 00318 /* This is a very simple hack that loads the (new, special) German 00319 tax definition file in a German locale, or (default) loads the 00320 previous US tax file. */ 00321 # ifdef G_OS_WIN32 00322 gchar *thislocale = g_win32_getlocale(); 00323 gboolean is_de_DE = (strncmp(thislocale, "de_DE", 5) == 0); 00324 g_free(thislocale); 00325 # else /* !G_OS_WIN32 */ 00326 const char *thislocale = setlocale(LC_ALL, NULL); 00327 gboolean is_de_DE = (strncmp(thislocale, "de_DE", 5) == 0); 00328 # endif /* G_OS_WIN32 */ 00329 #else /* LOCALE_SPECIFIC_TAX */ 00330 gboolean is_de_DE = FALSE; 00331 #endif /* LOCALE_SPECIFIC_TAX */ 00332 tax_module = is_de_DE ? 00333 "gnucash/tax/de_DE" : 00334 "gnucash/tax/us"; 00335 00336 module = gnc_module_load ((char *)tax_module, 0); 00337 00338 g_return_val_if_fail (module, NULL); 00339 00340 get_form = scm_c_eval_string 00341 ("(false-if-exception gnc:txf-get-form)"); 00342 get_desc = scm_c_eval_string 00343 ("(false-if-exception gnc:txf-get-description)"); 00344 } 00345 00346 g_return_val_if_fail (scm_is_procedure (get_form), NULL); 00347 g_return_val_if_fail (scm_is_procedure (get_desc), NULL); 00348 00349 category = scm_c_eval_string (atype == ACCT_TYPE_INCOME ? 00350 "txf-income-categories" : 00351 (atype == ACCT_TYPE_EXPENSE ? 00352 "txf-expense-categories" : 00353 (((atype == ACCT_TYPE_BANK) || 00354 (atype == ACCT_TYPE_CASH) || 00355 (atype == ACCT_TYPE_ASSET) || 00356 (atype == ACCT_TYPE_STOCK) || 00357 (atype == ACCT_TYPE_MUTUAL) || 00358 (atype == ACCT_TYPE_RECEIVABLE)) ? 00359 "txf-asset-categories" : 00360 (((atype == ACCT_TYPE_CREDIT) || 00361 (atype == ACCT_TYPE_LIABILITY) || 00362 (atype == ACCT_TYPE_EQUITY) || 00363 (atype == ACCT_TYPE_PAYABLE)) ? 00364 "txf-liab-eq-categories" : "")))); 00365 00366 if (g_str_has_prefix (code, prefix)) 00367 { 00368 const gchar *num_code_tmp; 00369 num_code_tmp = g_strdup (code); 00370 num_code_tmp++; /* to lose the leading N */ 00371 num_code = g_strdup (num_code_tmp); 00372 num_code_tmp--; 00373 g_free ((gpointer *) num_code_tmp); 00374 } 00375 else 00376 { 00377 num_code = g_strdup (code); 00378 } 00379 00380 if (category == SCM_UNDEFINED) 00381 { 00382 if (tax_related) 00383 return_string = g_strdup_printf 00384 (_("Tax type %s: invalid code %s for account type"), 00385 tax_type, num_code); 00386 else 00387 return_string = g_strdup_printf 00388 (_("Not tax-related; tax type %s: invalid code %s for account type"), 00389 tax_type, num_code); 00390 } 00391 else 00392 { 00393 SCM code_scm; 00394 SCM form_scm; 00395 code_scm = scm_str2symbol (code); 00396 form_scm = scm_call_3 (get_form, category, code_scm, tax_entity_type); 00397 if (!scm_is_string (form_scm)) 00398 { 00399 if (tax_related) 00400 return_string = g_strdup_printf 00401 (_("Invalid code %s for tax type %s"), 00402 num_code, tax_type); 00403 else 00404 return_string = g_strdup_printf 00405 (_("Not tax-related; invalid code %s for tax type %s"), 00406 num_code, tax_type); 00407 } 00408 else 00409 { 00410 gchar *form = NULL; 00411 scm_dynwind_begin (0); 00412 form = scm_to_locale_string (form_scm); 00413 if (!form) 00414 { 00415 if (tax_related) 00416 return_string = g_strdup_printf 00417 (_("No form: code %s, tax type %s"), num_code, 00418 tax_type); 00419 else 00420 return_string = g_strdup_printf 00421 (_("Not tax-related; no form: code %s, tax type %s"), 00422 num_code, tax_type); 00423 } 00424 else 00425 { 00426 SCM desc_scm; 00427 desc_scm = scm_call_3 (get_desc, category, code_scm, 00428 tax_entity_type); 00429 if (!scm_is_string (desc_scm)) 00430 { 00431 if (tax_related) 00432 return_string = g_strdup_printf 00433 (_("No description: form %s, code %s, tax type %s"), 00434 form, num_code, tax_type); 00435 else 00436 return_string = g_strdup_printf 00437 (_("Not tax-related; no description: form %s, code %s, tax type %s"), 00438 form, num_code, tax_type); 00439 } 00440 else 00441 { 00442 gchar *desc = NULL; 00443 desc = scm_to_locale_string (desc_scm); 00444 if (!desc) 00445 { 00446 if (tax_related) 00447 return_string = g_strdup_printf 00448 (_("No description: form %s, code %s, tax type %s"), 00449 form, num_code, tax_type); 00450 else 00451 return_string = g_strdup_printf 00452 (_("Not tax-related; no description: form %s, code %s, tax type %s"), 00453 form, num_code, tax_type); 00454 } 00455 else 00456 { 00457 gint64 copy_number; 00458 gchar *copy_txt = NULL; 00459 copy_number = xaccAccountGetTaxUSCopyNumber (account); 00460 copy_txt = (copy_number == 1) ? 00461 g_strdup ("") : 00462 g_strdup_printf ("(%d)", 00463 (gint) copy_number); 00464 if (tax_related) 00465 { 00466 if (safe_strcmp (form, "") == 0) 00467 return_string = g_strdup_printf ("%s", desc); 00468 else 00469 return_string = g_strdup_printf ("%s%s: %s", 00470 form, copy_txt, desc); 00471 } 00472 else 00473 { 00474 return_string = g_strdup_printf 00475 (_("Not tax-related; %s%s: %s (code %s, tax type %s)"), 00476 form, copy_txt, desc, num_code, tax_type); 00477 } 00478 g_free (copy_txt); 00479 } 00480 scm_dynwind_free (desc); 00481 } 00482 } 00483 scm_dynwind_free (form); 00484 scm_dynwind_end (); 00485 } 00486 } 00487 g_free (num_code); 00488 return return_string; 00489 } 00490 } 00491 00492 /* Caller is responsible for g_free'ing returned memory */ 00493 char * 00494 gnc_ui_account_get_tax_info_sub_acct_string (const Account *account) 00495 { 00496 GList *descendant, *account_descendants; 00497 00498 if (!account) 00499 return NULL; 00500 00501 account_descendants = gnc_account_get_descendants (account); 00502 if (account_descendants) 00503 { 00504 gint sub_acct_tax_number = 0; 00505 for (descendant = account_descendants; descendant; 00506 descendant = g_list_next(descendant)) 00507 { 00508 if (xaccAccountGetTaxRelated (descendant->data)) 00509 sub_acct_tax_number++; 00510 } 00511 g_list_free (account_descendants); 00512 g_list_free (descendant); 00513 /* Translators: This and the following strings appear on 00514 * the account tab if the Tax Info column is displayed, 00515 * i.e. if the user wants to record the tax form number 00516 * and location on that tax form which corresponds to this 00517 * gnucash account. For the US Income Tax support in 00518 * gnucash, each tax code that can be assigned to an 00519 * account generally corresponds to a specific line number 00520 * on a paper form and each form has a unique 00521 * identification (e.g., Form 1040, Schedule A). */ 00522 return (sub_acct_tax_number == 0) ? NULL : 00523 g_strdup_printf (_("(Tax-related subaccounts: %d)"), 00524 sub_acct_tax_number); 00525 } 00526 else 00527 return NULL; 00528 } 00529 00530 static const char * 00531 string_after_colon (const char *msgstr) 00532 { 00533 const char *string_at_colon; 00534 g_assert(msgstr); 00535 string_at_colon = strchr(msgstr, ':'); 00536 if (string_at_colon) 00537 return string_at_colon + 1; 00538 else 00539 /* No colon found; we assume the translation contains only the 00540 part after the colon, similar to the disambiguation prefixes */ 00541 return msgstr; 00542 } 00543 00544 /********************************************************************\ 00545 * gnc_get_reconcile_str * 00546 * return the i18n'd string for the given reconciled flag * 00547 * * 00548 * Args: reconciled_flag - the flag to convert into a string * 00549 * Returns: the i18n'd reconciled string * 00550 \********************************************************************/ 00551 const char * 00552 gnc_get_reconcile_str (char reconciled_flag) 00553 { 00554 switch (reconciled_flag) 00555 { 00556 case NREC: 00557 /* Translators: For the following strings, the single letters 00558 after the colon are abbreviations of the word before the 00559 colon. You should only translate the letter *after* the colon. */ 00560 return string_after_colon(_("not cleared:n")); 00561 case CREC: 00562 /* Translators: Please only translate the letter *after* the colon. */ 00563 return string_after_colon(_("cleared:c")); 00564 case YREC: 00565 /* Translators: Please only translate the letter *after* the colon. */ 00566 return string_after_colon(_("reconciled:y")); 00567 case FREC: 00568 /* Translators: Please only translate the letter *after* the colon. */ 00569 return string_after_colon(_("frozen:f")); 00570 case VREC: 00571 /* Translators: Please only translate the letter *after* the colon. */ 00572 return string_after_colon(_("void:v")); 00573 default: 00574 PERR("Bad reconciled flag\n"); 00575 return NULL; 00576 } 00577 } 00578 00579 /********************************************************************\ 00580 * gnc_get_reconcile_valid_flags * 00581 * return a string containing the list of reconciled flags * 00582 * * 00583 * Returns: the i18n'd reconciled flags string * 00584 \********************************************************************/ 00585 const char * 00586 gnc_get_reconcile_valid_flags (void) 00587 { 00588 static const char flags[] = { NREC, CREC, YREC, FREC, VREC, 0 }; 00589 return flags; 00590 } 00591 00592 /********************************************************************\ 00593 * gnc_get_reconcile_flag_order * 00594 * return a string containing the reconciled-flag change order * 00595 * * 00596 * Args: reconciled_flag - the flag to convert into a string * 00597 * Returns: the i18n'd reconciled string * 00598 \********************************************************************/ 00599 const char * 00600 gnc_get_reconcile_flag_order (void) 00601 { 00602 static const char flags[] = { NREC, CREC, 0 }; 00603 return flags; 00604 } 00605 00606 00607 static const char * 00608 equity_base_name (GNCEquityType equity_type) 00609 { 00610 switch (equity_type) 00611 { 00612 case EQUITY_OPENING_BALANCE: 00613 return N_("Opening Balances"); 00614 00615 case EQUITY_RETAINED_EARNINGS: 00616 return N_("Retained Earnings"); 00617 00618 default: 00619 return NULL; 00620 } 00621 } 00622 00623 Account * 00624 gnc_find_or_create_equity_account (Account *root, 00625 GNCEquityType equity_type, 00626 gnc_commodity *currency) 00627 { 00628 Account *parent; 00629 Account *account; 00630 gboolean name_exists; 00631 gboolean base_name_exists; 00632 const char *base_name; 00633 char *name; 00634 00635 g_return_val_if_fail (equity_type >= 0, NULL); 00636 g_return_val_if_fail (equity_type < NUM_EQUITY_TYPES, NULL); 00637 g_return_val_if_fail (currency != NULL, NULL); 00638 g_return_val_if_fail (root != NULL, NULL); 00639 00640 base_name = equity_base_name (equity_type); 00641 00642 account = gnc_account_lookup_by_name(root, base_name); 00643 if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY) 00644 account = NULL; 00645 00646 if (!account) 00647 { 00648 base_name = base_name && *base_name ? _(base_name) : ""; 00649 00650 account = gnc_account_lookup_by_name(root, base_name); 00651 if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY) 00652 account = NULL; 00653 } 00654 00655 base_name_exists = (account != NULL); 00656 00657 if (account && 00658 gnc_commodity_equiv (currency, xaccAccountGetCommodity (account))) 00659 return account; 00660 00661 name = g_strconcat (base_name, " - ", 00662 gnc_commodity_get_mnemonic (currency), NULL); 00663 account = gnc_account_lookup_by_name(root, name); 00664 if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY) 00665 account = NULL; 00666 00667 name_exists = (account != NULL); 00668 00669 if (account && 00670 gnc_commodity_equiv (currency, xaccAccountGetCommodity (account))) 00671 return account; 00672 00673 /* Couldn't find one, so create it */ 00674 if (name_exists && base_name_exists) 00675 { 00676 PWARN ("equity account with unexpected currency"); 00677 g_free (name); 00678 return NULL; 00679 } 00680 00681 if (!base_name_exists && 00682 gnc_commodity_equiv (currency, gnc_default_currency ())) 00683 { 00684 g_free (name); 00685 name = g_strdup (base_name); 00686 } 00687 00688 parent = gnc_account_lookup_by_name(root, _("Equity")); 00689 if (!parent || xaccAccountGetType (parent) != ACCT_TYPE_EQUITY) 00690 parent = root; 00691 g_assert(parent); 00692 00693 account = xaccMallocAccount (gnc_account_get_book(root)); 00694 00695 xaccAccountBeginEdit (account); 00696 00697 xaccAccountSetName (account, name); 00698 xaccAccountSetType (account, ACCT_TYPE_EQUITY); 00699 xaccAccountSetCommodity (account, currency); 00700 00701 xaccAccountBeginEdit (parent); 00702 gnc_account_append_child (parent, account); 00703 xaccAccountCommitEdit (parent); 00704 00705 xaccAccountCommitEdit (account); 00706 00707 g_free (name); 00708 00709 return account; 00710 } 00711 00712 gboolean 00713 gnc_account_create_opening_balance (Account *account, 00714 gnc_numeric balance, 00715 time_t date, 00716 QofBook *book) 00717 { 00718 Account *equity_account; 00719 Transaction *trans; 00720 Split *split; 00721 00722 if (gnc_numeric_zero_p (balance)) 00723 return TRUE; 00724 00725 g_return_val_if_fail (account != NULL, FALSE); 00726 00727 equity_account = 00728 gnc_find_or_create_equity_account (gnc_account_get_root(account), 00729 EQUITY_OPENING_BALANCE, 00730 xaccAccountGetCommodity (account)); 00731 if (!equity_account) 00732 return FALSE; 00733 00734 xaccAccountBeginEdit (account); 00735 xaccAccountBeginEdit (equity_account); 00736 00737 trans = xaccMallocTransaction (book); 00738 00739 xaccTransBeginEdit (trans); 00740 00741 xaccTransSetCurrency (trans, xaccAccountGetCommodity (account)); 00742 xaccTransSetDatePostedSecs (trans, date); 00743 xaccTransSetDescription (trans, _("Opening Balance")); 00744 00745 split = xaccMallocSplit (book); 00746 00747 xaccTransAppendSplit (trans, split); 00748 xaccAccountInsertSplit (account, split); 00749 00750 xaccSplitSetAmount (split, balance); 00751 xaccSplitSetValue (split, balance); 00752 00753 balance = gnc_numeric_neg (balance); 00754 00755 split = xaccMallocSplit (book); 00756 00757 xaccTransAppendSplit (trans, split); 00758 xaccAccountInsertSplit (equity_account, split); 00759 00760 xaccSplitSetAmount (split, balance); 00761 xaccSplitSetValue (split, balance); 00762 00763 xaccTransCommitEdit (trans); 00764 xaccAccountCommitEdit (equity_account); 00765 xaccAccountCommitEdit (account); 00766 00767 return TRUE; 00768 } 00769 00770 static void 00771 gnc_lconv_set_utf8 (char **p_value, char *default_value) 00772 { 00773 char *value = *p_value; 00774 *p_value = NULL; 00775 00776 if ((value == NULL) || (value[0] == 0)) 00777 value = default_value; 00778 00779 #ifdef G_OS_WIN32 00780 { 00781 /* get number of resulting wide characters */ 00782 size_t count = mbstowcs (NULL, value, 0); 00783 if (count > 0) 00784 { 00785 /* malloc and convert */ 00786 wchar_t *wvalue = g_malloc ((count + 1) * sizeof(wchar_t)); 00787 count = mbstowcs (wvalue, value, count + 1); 00788 if (count > 0) 00789 { 00790 *p_value = g_utf16_to_utf8 (wvalue, -1, NULL, NULL, NULL); 00791 } 00792 g_free (wvalue); 00793 } 00794 } 00795 #else /* !G_OS_WIN32 */ 00796 *p_value = g_locale_to_utf8 (value, -1, NULL, NULL, NULL); 00797 #endif 00798 00799 if (*p_value == NULL) 00800 { 00801 // The g_locale_to_utf8 conversion failed. FIXME: Should we rather 00802 // use an empty string instead of the default_value? Not sure. 00803 *p_value = default_value; 00804 } 00805 } 00806 00807 static void 00808 gnc_lconv_set_char (char *p_value, char default_value) 00809 { 00810 if ((p_value != NULL) && (*p_value == CHAR_MAX)) 00811 *p_value = default_value; 00812 } 00813 00814 00815 gnc_commodity * 00816 gnc_locale_default_currency_nodefault (void) 00817 { 00818 gnc_commodity * currency; 00819 gnc_commodity_table *table; 00820 const char *code; 00821 00822 table = gnc_get_current_commodities (); 00823 code = gnc_locale_default_iso_currency_code (); 00824 00825 currency = gnc_commodity_table_lookup (table, GNC_COMMODITY_NS_CURRENCY, code); 00826 00827 /* Some very old locales (notably on win32) still announce a euro 00828 currency as default, although it has been replaced by EUR in 00829 2001. We use EUR as default in that case, but the user can always 00830 override from gconf. */ 00831 if (gnc_is_euro_currency (currency)) 00832 currency = gnc_get_euro(); 00833 00834 return (currency ? currency : NULL); 00835 } 00836 00837 gnc_commodity * 00838 gnc_locale_default_currency (void) 00839 { 00840 gnc_commodity * currency = gnc_locale_default_currency_nodefault (); 00841 00842 return (currency ? currency : 00843 gnc_commodity_table_lookup (gnc_get_current_commodities (), 00844 GNC_COMMODITY_NS_CURRENCY, "USD")); 00845 } 00846 00847 00848 static gnc_commodity * 00849 gnc_default_currency_common (gchar *requested_currency, 00850 const gchar *gconf_section) 00851 { 00852 gnc_commodity *currency = NULL; 00853 gchar *choice, *mnemonic; 00854 00855 if (requested_currency) 00856 return gnc_commodity_table_lookup(gnc_get_current_commodities(), 00857 GNC_COMMODITY_NS_CURRENCY, 00858 requested_currency); 00859 00860 choice = gnc_gconf_get_string(gconf_section, KEY_CURRENCY_CHOICE, NULL); 00861 if (g_strcmp0(choice, "other") == 0) 00862 { 00863 mnemonic = gnc_gconf_get_string(gconf_section, KEY_CURRENCY_OTHER, NULL); 00864 currency = gnc_commodity_table_lookup(gnc_get_current_commodities(), 00865 GNC_COMMODITY_NS_CURRENCY, mnemonic); 00866 DEBUG("mnemonic %s, result %p", mnemonic ? mnemonic : "(null)", currency); 00867 g_free(mnemonic); 00868 } 00869 g_free(choice); 00870 00871 if (!currency) 00872 currency = gnc_locale_default_currency (); 00873 if (currency) 00874 { 00875 mnemonic = requested_currency; 00876 // ??? Does anyone know what this is supposed to be doing? 00877 // requested_currency = g_strdup(gnc_commodity_get_mnemonic(currency)); 00878 g_free(mnemonic); 00879 } 00880 return currency; 00881 } 00882 00883 gnc_commodity * 00884 gnc_default_currency (void) 00885 { 00886 return gnc_default_currency_common (user_default_currency, GCONF_GENERAL); 00887 } 00888 00889 gnc_commodity * 00890 gnc_default_report_currency (void) 00891 { 00892 return gnc_default_currency_common (user_report_currency, GCONF_GENERAL_REPORT); 00893 } 00894 00895 static void 00896 gnc_currency_changed_cb (GConfEntry *entry, gpointer user_data) 00897 { 00898 user_default_currency = NULL; 00899 user_report_currency = NULL; 00900 gnc_hook_run(HOOK_CURRENCY_CHANGED, NULL); 00901 } 00902 00903 00904 GNCPrintAmountInfo 00905 gnc_default_print_info (gboolean use_symbol) 00906 { 00907 static GNCPrintAmountInfo info; 00908 static gboolean got_it = FALSE; 00909 struct lconv *lc; 00910 00911 /* These must be updated each time. */ 00912 info.use_symbol = use_symbol ? 1 : 0; 00913 info.commodity = gnc_default_currency (); 00914 00915 if (got_it) 00916 return info; 00917 00918 lc = gnc_localeconv (); 00919 00920 info.max_decimal_places = lc->frac_digits; 00921 info.min_decimal_places = lc->frac_digits; 00922 00923 info.use_separators = 1; 00924 info.use_locale = 1; 00925 info.monetary = 1; 00926 info.force_fit = 0; 00927 info.round = 0; 00928 00929 got_it = TRUE; 00930 00931 return info; 00932 } 00933 00934 static gboolean 00935 is_decimal_fraction (int fraction, guint8 *max_decimal_places_p) 00936 { 00937 guint8 max_decimal_places = 0; 00938 00939 if (fraction <= 0) 00940 return FALSE; 00941 00942 while (fraction != 1) 00943 { 00944 if (fraction % 10 != 0) 00945 return FALSE; 00946 00947 fraction = fraction / 10; 00948 max_decimal_places += 1; 00949 } 00950 00951 if (max_decimal_places_p) 00952 *max_decimal_places_p = max_decimal_places; 00953 00954 return TRUE; 00955 } 00956 00957 GNCPrintAmountInfo 00958 gnc_commodity_print_info (const gnc_commodity *commodity, 00959 gboolean use_symbol) 00960 { 00961 GNCPrintAmountInfo info; 00962 gboolean is_iso; 00963 00964 if (commodity == NULL) 00965 return gnc_default_print_info (use_symbol); 00966 00967 info.commodity = commodity; 00968 00969 is_iso = gnc_commodity_is_iso (commodity); 00970 00971 if (is_decimal_fraction (gnc_commodity_get_fraction (commodity), 00972 &info.max_decimal_places)) 00973 { 00974 if (is_iso) 00975 info.min_decimal_places = info.max_decimal_places; 00976 else 00977 info.min_decimal_places = 0; 00978 } 00979 else 00980 info.max_decimal_places = info.min_decimal_places = 0; 00981 00982 info.use_separators = 1; 00983 info.use_symbol = use_symbol ? 1 : 0; 00984 info.use_locale = is_iso ? 1 : 0; 00985 info.monetary = 1; 00986 info.force_fit = 0; 00987 info.round = 0; 00988 00989 return info; 00990 } 00991 00992 static GNCPrintAmountInfo 00993 gnc_account_print_info_helper(const Account *account, gboolean use_symbol, 00994 gnc_commodity * (*efffunc)(const Account *), 00995 int (*scufunc)(const Account*)) 00996 { 00997 GNCPrintAmountInfo info; 00998 gboolean is_iso; 00999 int scu; 01000 01001 if (account == NULL) 01002 return gnc_default_print_info (use_symbol); 01003 01004 info.commodity = efffunc (account); 01005 01006 is_iso = gnc_commodity_is_iso (info.commodity); 01007 01008 scu = scufunc (account); 01009 01010 if (is_decimal_fraction (scu, &info.max_decimal_places)) 01011 { 01012 if (is_iso) 01013 info.min_decimal_places = info.max_decimal_places; 01014 else 01015 info.min_decimal_places = 0; 01016 } 01017 else 01018 info.max_decimal_places = info.min_decimal_places = 0; 01019 01020 info.use_separators = 1; 01021 info.use_symbol = use_symbol ? 1 : 0; 01022 info.use_locale = is_iso ? 1 : 0; 01023 info.monetary = 1; 01024 info.force_fit = 0; 01025 info.round = 0; 01026 01027 return info; 01028 } 01029 01030 GNCPrintAmountInfo 01031 gnc_account_print_info (const Account *account, gboolean use_symbol) 01032 { 01033 return gnc_account_print_info_helper(account, use_symbol, 01034 xaccAccountGetCommodity, 01035 xaccAccountGetCommoditySCU); 01036 } 01037 01038 GNCPrintAmountInfo 01039 gnc_split_amount_print_info (Split *split, gboolean use_symbol) 01040 { 01041 if (!split) 01042 { 01043 GNCPrintAmountInfo info = gnc_default_share_print_info (); 01044 info.use_symbol = use_symbol; 01045 return info; 01046 } 01047 01048 return gnc_account_print_info (xaccSplitGetAccount (split), use_symbol); 01049 } 01050 01051 static GNCPrintAmountInfo 01052 gnc_default_print_info_helper (int decplaces) 01053 { 01054 GNCPrintAmountInfo info; 01055 01056 info.commodity = NULL; 01057 01058 info.max_decimal_places = decplaces; 01059 info.min_decimal_places = 0; 01060 01061 info.use_separators = 1; 01062 info.use_symbol = 0; 01063 info.use_locale = 1; 01064 info.monetary = 1; 01065 info.force_fit = 0; 01066 info.round = 0; 01067 01068 return info; 01069 } 01070 01071 GNCPrintAmountInfo 01072 gnc_default_share_print_info (void) 01073 { 01074 static GNCPrintAmountInfo info; 01075 static gboolean got_it = FALSE; 01076 01077 if (!got_it) 01078 { 01079 info = gnc_default_print_info_helper (5); 01080 got_it = TRUE; 01081 } 01082 01083 return info; 01084 } 01085 01086 GNCPrintAmountInfo 01087 gnc_share_print_info_places (int decplaces) 01088 { 01089 GNCPrintAmountInfo info; 01090 01091 info = gnc_default_share_print_info (); 01092 info.max_decimal_places = decplaces; 01093 info.min_decimal_places = decplaces; 01094 info.force_fit = 1; 01095 info.round = 1; 01096 return info; 01097 } 01098 01099 GNCPrintAmountInfo 01100 gnc_default_price_print_info (void) 01101 { 01102 static GNCPrintAmountInfo info; 01103 static gboolean got_it = FALSE; 01104 01105 if (!got_it) 01106 { 01107 info = gnc_default_print_info_helper (6); 01108 got_it = TRUE; 01109 } 01110 01111 return info; 01112 } 01113 01114 GNCPrintAmountInfo 01115 gnc_integral_print_info (void) 01116 { 01117 static GNCPrintAmountInfo info; 01118 static gboolean got_it = FALSE; 01119 01120 if (!got_it) 01121 { 01122 info = gnc_default_print_info_helper (0); 01123 got_it = TRUE; 01124 } 01125 01126 return info; 01127 } 01128 01129 /* Utility function for printing non-negative amounts */ 01130 static int 01131 PrintAmountInternal(char *buf, gnc_numeric val, const GNCPrintAmountInfo *info) 01132 { 01133 struct lconv *lc = gnc_localeconv(); 01134 int num_whole_digits; 01135 char temp_buf[128]; 01136 gnc_numeric whole, rounding; 01137 int min_dp, max_dp; 01138 gboolean value_is_negative, value_is_decimal; 01139 01140 g_return_val_if_fail (info != NULL, 0); 01141 01142 if (gnc_numeric_check (val)) 01143 { 01144 PWARN ("Bad numeric: %s.", 01145 gnc_numeric_errorCode_to_string(gnc_numeric_check (val))); 01146 *buf = '\0'; 01147 return 0; 01148 } 01149 01150 /* Print the absolute value, but remember sign */ 01151 value_is_negative = gnc_numeric_negative_p (val); 01152 val = gnc_numeric_abs (val); 01153 01154 /* Try to print as decimal. */ 01155 value_is_decimal = gnc_numeric_to_decimal(&val, NULL); 01156 01157 /* Force at least auto_decimal_places zeros */ 01158 if (auto_decimal_enabled) 01159 { 01160 min_dp = MAX(auto_decimal_places, info->min_decimal_places); 01161 max_dp = MAX(auto_decimal_places, info->max_decimal_places); 01162 } 01163 else 01164 { 01165 min_dp = info->min_decimal_places; 01166 max_dp = info->max_decimal_places; 01167 } 01168 01169 /* Don to limit the number of decimal places _UNLESS_ force_fit is 01170 * true. */ 01171 if (!info->force_fit) 01172 max_dp = 99; 01173 01174 /* rounding? -- can only ROUND if force_fit is also true */ 01175 if (value_is_decimal && info->round && info->force_fit) 01176 { 01177 gnc_numeric oldval = val; 01178 rounding.num = 5; /* Limit the denom to 10^13 ~= 2^44, leaving max at ~524288 */ 01179 rounding.denom = pow(10, max_dp + 1); 01180 val = gnc_numeric_add(val, rounding, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01181 /* Yes, rounding up can cause overflow. Check for it. */ 01182 if (gnc_numeric_check(val)) 01183 { 01184 PWARN ("Bad numeric from rounding: %s.", 01185 gnc_numeric_errorCode_to_string(gnc_numeric_check (val))); 01186 *buf = '\0'; 01187 return 0; 01188 } 01189 } 01190 01191 /* calculate the integer part and the remainder */ 01192 whole = gnc_numeric_convert(val, 1, GNC_HOW_RND_TRUNC); 01193 val = gnc_numeric_sub (val, whole, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER); 01194 if (gnc_numeric_check (val)) 01195 { 01196 PWARN ("Problem with remainder: %s.", 01197 gnc_numeric_errorCode_to_string(gnc_numeric_check (val))); 01198 *buf = '\0'; 01199 return 0; 01200 } 01201 01202 /* print the integer part without separators */ 01203 sprintf(temp_buf, "%" G_GINT64_FORMAT, whole.num); 01204 num_whole_digits = strlen (temp_buf); 01205 01206 if (!info->use_separators) 01207 strcpy (buf, temp_buf); 01208 else 01209 { 01210 int group_count; 01211 char *separator; 01212 char *temp_ptr; 01213 char *buf_ptr; 01214 char *group; 01215 gchar *rev_buf; 01216 01217 if (info->monetary) 01218 { 01219 separator = lc->mon_thousands_sep; 01220 group = lc->mon_grouping; 01221 } 01222 else 01223 { 01224 separator = lc->thousands_sep; 01225 group = lc->grouping; 01226 } 01227 01228 buf_ptr = buf; 01229 temp_ptr = &temp_buf[num_whole_digits - 1]; 01230 group_count = 0; 01231 01232 while (temp_ptr != temp_buf) 01233 { 01234 *buf_ptr++ = *temp_ptr--; 01235 01236 if (*group != CHAR_MAX) 01237 { 01238 group_count++; 01239 01240 if (group_count == *group) 01241 { 01242 g_utf8_strncpy(buf_ptr, separator, 1); 01243 buf_ptr = g_utf8_find_next_char(buf_ptr, NULL); 01244 group_count = 0; 01245 01246 /* Peek ahead at the next group code */ 01247 switch (group[1]) 01248 { 01249 /* A null char means repeat the last group indefinitely */ 01250 case '\0': 01251 break; 01252 /* CHAR_MAX means no more grouping allowed */ 01253 case CHAR_MAX: 01254 /* fall through */ 01255 /* Anything else means another group size */ 01256 default: 01257 group++; 01258 break; 01259 } 01260 } 01261 } 01262 } 01263 01264 /* We built the string backwards, now reverse */ 01265 *buf_ptr++ = *temp_ptr; 01266 *buf_ptr = '\0'; 01267 rev_buf = g_utf8_strreverse(buf, -1); 01268 strcpy (buf, rev_buf); 01269 g_free(rev_buf); 01270 } /* endif */ 01271 01272 /* at this point, buf contains the whole part of the number */ 01273 01274 /* If it's not decimal, print the fraction as an expression. */ 01275 if (!value_is_decimal) 01276 { 01277 val = gnc_numeric_reduce (val); 01278 01279 if (val.denom > 0) 01280 sprintf (temp_buf, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, 01281 val.num, val.denom); 01282 else 01283 sprintf (temp_buf, "%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT, 01284 val.num, -val.denom); 01285 01286 if (whole.num == 0) 01287 *buf = '\0'; 01288 else if (value_is_negative) 01289 strcat(buf, " - "); 01290 else 01291 strcat(buf, " + "); 01292 01293 strcat (buf, temp_buf); 01294 } 01295 else 01296 { 01297 char *decimal_point; 01298 guint8 num_decimal_places = 0; 01299 char *temp_ptr = temp_buf; 01300 01301 decimal_point = info->monetary 01302 ? lc->mon_decimal_point 01303 : lc->decimal_point; 01304 g_utf8_strncpy(temp_ptr, decimal_point, 1); 01305 temp_ptr = g_utf8_find_next_char(temp_ptr, NULL); 01306 01307 while (!gnc_numeric_zero_p (val) 01308 && (val.denom != 1) 01309 && (num_decimal_places < max_dp)) 01310 { 01311 gint64 digit; 01312 01313 val.denom = val.denom / 10; 01314 01315 digit = val.num / val.denom; 01316 01317 *temp_ptr++ = digit + '0'; 01318 num_decimal_places++; 01319 01320 val.num = val.num - (digit * val.denom); 01321 } 01322 01323 while (num_decimal_places < min_dp) 01324 { 01325 *temp_ptr++ = '0'; 01326 num_decimal_places++; 01327 } 01328 01329 /* cap the end and move to the last character */ 01330 *temp_ptr-- = '\0'; 01331 01332 /* Here we strip off trailing decimal zeros per the argument. */ 01333 while (*temp_ptr == '0' && num_decimal_places > min_dp) 01334 { 01335 *temp_ptr-- = '\0'; 01336 num_decimal_places--; 01337 } 01338 01339 if (num_decimal_places > max_dp) 01340 { 01341 PWARN ("max_decimal_places too small; limit %d, value %s%s", 01342 info->max_decimal_places, buf, temp_buf); 01343 } 01344 01345 if (num_decimal_places > 0) 01346 strcat (buf, temp_buf); 01347 } 01348 01349 return strlen(buf); 01350 } 01351 01355 int 01356 xaccSPrintAmount (char * bufp, gnc_numeric val, GNCPrintAmountInfo info) 01357 { 01358 struct lconv *lc; 01359 01360 char *orig_bufp = bufp; 01361 const char *currency_symbol; 01362 const char *sign; 01363 01364 char cs_precedes; 01365 char sep_by_space; 01366 char sign_posn; 01367 01368 gboolean print_sign = TRUE; 01369 gboolean is_shares = FALSE; 01370 gboolean print_absolute = FALSE; 01371 01372 if (!bufp) 01373 return 0; 01374 01375 lc = gnc_localeconv(); 01376 01377 if (info.use_symbol) 01378 { 01379 /* There was a bug here: don't use gnc_locale_default_currency */ 01380 if (gnc_commodity_equiv (info.commodity, 01381 gnc_locale_default_currency_nodefault ())) 01382 { 01383 currency_symbol = lc->currency_symbol; 01384 } 01385 else 01386 { 01387 if (info.commodity && !gnc_commodity_is_iso (info.commodity)) 01388 is_shares = TRUE; 01389 01390 currency_symbol = gnc_commodity_get_mnemonic (info.commodity); 01391 info.use_locale = 0; 01392 } 01393 01394 if (currency_symbol == NULL) 01395 currency_symbol = ""; 01396 } 01397 else 01398 currency_symbol = NULL; 01399 01400 if (!info.use_locale) 01401 { 01402 cs_precedes = is_shares ? 0 : 1; 01403 sep_by_space = 1; 01404 } 01405 else 01406 { 01407 if (gnc_numeric_negative_p (val)) 01408 { 01409 cs_precedes = lc->n_cs_precedes; 01410 sep_by_space = lc->n_sep_by_space; 01411 } 01412 else 01413 { 01414 cs_precedes = lc->p_cs_precedes; 01415 sep_by_space = lc->p_sep_by_space; 01416 } 01417 } 01418 01419 if (gnc_numeric_negative_p (val)) 01420 { 01421 sign = lc->negative_sign; 01422 sign_posn = lc->n_sign_posn; 01423 } 01424 else 01425 { 01426 sign = lc->positive_sign; 01427 sign_posn = lc->p_sign_posn; 01428 } 01429 01430 if (gnc_numeric_zero_p (val) || (sign == NULL) || (sign[0] == 0)) 01431 print_sign = FALSE; 01432 01433 /* See if we print sign now */ 01434 if (print_sign && (sign_posn == 1)) 01435 bufp = g_stpcpy(bufp, sign); 01436 01437 /* Now see if we print currency */ 01438 if (cs_precedes) 01439 { 01440 /* See if we print sign now */ 01441 if (print_sign && (sign_posn == 3)) 01442 bufp = g_stpcpy(bufp, sign); 01443 01444 if (info.use_symbol) 01445 { 01446 bufp = g_stpcpy(bufp, currency_symbol); 01447 if (sep_by_space) 01448 bufp = g_stpcpy(bufp, " "); 01449 } 01450 01451 /* See if we print sign now */ 01452 if (print_sign && (sign_posn == 4)) 01453 bufp = g_stpcpy(bufp, sign); 01454 } 01455 01456 /* Now see if we print parentheses */ 01457 if (print_sign && (sign_posn == 0)) 01458 { 01459 bufp = g_stpcpy(bufp, "("); 01460 print_absolute = TRUE; 01461 } 01462 01463 /* Now print the value */ 01464 bufp += PrintAmountInternal(bufp, 01465 print_absolute ? gnc_numeric_abs(val) : val, 01466 &info); 01467 01468 /* Now see if we print parentheses */ 01469 if (print_sign && (sign_posn == 0)) 01470 bufp = g_stpcpy(bufp, ")"); 01471 01472 /* Now see if we print currency */ 01473 if (!cs_precedes) 01474 { 01475 /* See if we print sign now */ 01476 if (print_sign && (sign_posn == 3)) 01477 bufp = g_stpcpy(bufp, sign); 01478 01479 if (info.use_symbol) 01480 { 01481 if (sep_by_space) 01482 bufp = g_stpcpy(bufp, " "); 01483 bufp = g_stpcpy(bufp, currency_symbol); 01484 } 01485 01486 /* See if we print sign now */ 01487 if (print_sign && (sign_posn == 4)) 01488 bufp = g_stpcpy(bufp, sign); 01489 } 01490 01491 /* See if we print sign now */ 01492 if (print_sign && (sign_posn == 2)) 01493 bufp = g_stpcpy(bufp, sign); 01494 01495 /* return length of printed string */ 01496 return (bufp - orig_bufp); 01497 } 01498 01499 const char * 01500 xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info) 01501 { 01502 /* hack alert -- this is not thread safe ... */ 01503 static char buf[1024]; 01504 01505 if (!xaccSPrintAmount (buf, val, info)) 01506 buf[0] = '\0'; 01507 01508 /* its OK to return buf, since we declared it static */ 01509 return buf; 01510 } 01511 01512 01513 /********************************************************************\ 01514 ********************************************************************/ 01515 01516 #define FUDGE .00001 01517 01518 /* This function is basically untranslatable. I'd 01519 guess out of the 29 translations we have, 20 will have their number 01520 wordings in a totally different way than English has (not to 01521 mention gender-dependent number endings). Which means this 01522 word-by-word translation will be useless or even plain 01523 wrong. For this reason, we don't even start to pretend a 01524 word-by-word translation would be of any use, so we don't mark any 01525 of these strings for translation. cstim, 2007-04-15. */ 01526 static gchar *small_numbers[] = 01527 { 01528 /* Translators: This section is for generating the "amount, in 01529 words" field when printing a check. This function gets the 01530 wording right for English, but unfortunately not for most other 01531 languages. Decide for yourself whether the check printing is 01532 actually needed in your language; if not, you can safely skip the 01533 translation of all of these strings. */ 01534 "Zero", "One", "Two", "Three", "Four", 01535 "Five", "Six", "Seven", "Eight", "Nine", 01536 "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", 01537 "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen", 01538 "Twenty" 01539 }; 01540 static gchar *medium_numbers[] = 01541 { 01542 "Zero", "Ten", "Twenty", "Thirty", "Forty", 01543 "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" 01544 }; 01545 static gchar *big_numbers[] = 01546 { 01547 /* Translators: This is the word for the number 10^2 */ 01548 "Hundred", 01549 /* Translators: This is the word for the number 10^3 */ 01550 "Thousand", 01551 /* Translators: This is the word for the number 10^6, one thousand 01552 thousands. */ 01553 "Million", 01554 /* Translators: This is the word for the number 10^9, one thousand 01555 millions. WATCH OUT: In British English and many other languages 01556 this word is used for 10^12 which is one million millions! In 01557 contrast to this, here in GnuCash this is used in the American 01558 English meaning of 10^9. */ 01559 "Billion", 01560 /* Translators: This is the word for the number 10^12, one million 01561 millions. */ 01562 "Trillion", 01563 /* Translators: This is the word for the number 10^15 */ 01564 "Quadrillion", 01565 /* Translators: This is the word for the number 10^18 */ 01566 "Quintillion" 01567 }; 01568 01569 static gchar * 01570 integer_to_words(gint64 val) 01571 { 01572 gint64 log_val, pow_val, this_part; 01573 GString *result; 01574 gchar *tmp; 01575 01576 if (val == 0) 01577 return g_strdup("zero"); 01578 if (val < 0) 01579 val = -val; 01580 01581 result = g_string_sized_new(100); 01582 01583 while (val >= 1000) 01584 { 01585 log_val = log10(val) / 3 + FUDGE; 01586 pow_val = exp(log_val * 3 * G_LN10) + FUDGE; 01587 this_part = val / pow_val; 01588 val -= this_part * pow_val; 01589 tmp = integer_to_words(this_part); 01590 g_string_append_printf(result, "%s %s ", tmp, 01591 gettext(big_numbers[log_val])); 01592 g_free(tmp); 01593 } 01594 01595 if (val >= 100) 01596 { 01597 this_part = val / 100; 01598 val -= this_part * 100; 01599 g_string_append_printf(result, "%s %s ", 01600 gettext(small_numbers[this_part]), 01601 gettext(big_numbers[0])); 01602 } 01603 01604 if (val > 20) 01605 { 01606 this_part = val / 10; 01607 val -= this_part * 10; 01608 g_string_append(result, gettext(medium_numbers[this_part])); 01609 g_string_append_c(result, ' '); 01610 } 01611 01612 if (val > 0) 01613 { 01614 this_part = val; 01615 val -= this_part; 01616 g_string_append(result, gettext(small_numbers[this_part])); 01617 g_string_append_c(result, ' '); 01618 } 01619 01620 result = g_string_truncate(result, result->len - 1); 01621 return g_string_free(result, FALSE); 01622 } 01623 01624 #ifdef _MSC_VER 01625 static double round(double x) 01626 { 01627 // A simple round() implementation because MSVC doesn't seem to have that 01628 return floor(x + 0.5); 01629 } 01630 #endif 01631 01632 gchar * 01633 number_to_words(gdouble val, gint64 denom) 01634 { 01635 gint64 int_part, frac_part; 01636 gchar *int_string, *nomin_string, *denom_string, *full_string; 01637 01638 if (val < 0) val = -val; 01639 if (denom < 0) denom = -denom; 01640 01641 int_part = floor(val); 01642 frac_part = round((val - int_part) * denom); 01643 01644 int_string = integer_to_words(int_part); 01645 /* Inside of the gettext macro _(...) we must not use any macros but 01646 only plain string literals. For this reason, convert the strings 01647 separately. */ 01648 nomin_string = g_strdup_printf("%02" G_GINT64_FORMAT, frac_part); 01649 denom_string = g_strdup_printf("%" G_GINT64_FORMAT, denom); 01650 full_string = 01651 /* Translators: This is for the "amount, in words" field in check 01652 printing. The first %s is the integer amount of dollars (or 01653 whatever currency), the second and third %s the cent amount as 01654 a fraction, e.g. 47/100. */ 01655 g_strdup_printf("%s and %s/%s", 01656 int_string, nomin_string, denom_string); 01657 g_free(int_string); 01658 g_free(nomin_string); 01659 g_free(denom_string); 01660 return full_string; 01661 } 01662 01663 gchar * 01664 numeric_to_words(gnc_numeric val) 01665 { 01666 return number_to_words(gnc_numeric_to_double(val), 01667 gnc_numeric_denom(val)); 01668 } 01669 01670 const gchar * 01671 printable_value (gdouble val, gint denom) 01672 { 01673 GNCPrintAmountInfo info; 01674 gnc_numeric num; 01675 01676 num = gnc_numeric_create(round(val * denom), denom); 01677 info = gnc_share_print_info_places(log10(denom)); 01678 return xaccPrintAmount (num, info); 01679 } 01680 01681 01682 /********************************************************************\ 01683 * xaccParseAmount * 01684 * parses amount strings using locale data * 01685 * * 01686 * Args: in_str -- pointer to string rep of num * 01687 * monetary -- boolean indicating whether value is monetary * 01688 * result -- pointer to result location, may be NULL * 01689 * endstr -- used to store first digit not used in parsing * 01690 * Return: gboolean -- TRUE if a number found and parsed * 01691 * If FALSE, result is not changed * 01692 \********************************************************************/ 01693 01694 /* Parsing state machine states */ 01695 typedef enum 01696 { 01697 START_ST, /* Parsing initial whitespace */ 01698 NEG_ST, /* Parsed a negative sign or a left paren */ 01699 PRE_GROUP_ST, /* Parsing digits before grouping and decimal characters */ 01700 START_GROUP_ST, /* Start of a digit group encountered (possibly) */ 01701 IN_GROUP_ST, /* Within a digit group */ 01702 FRAC_ST, /* Parsing the fractional portion of a number */ 01703 DONE_ST, /* Finished, number is correct module grouping constraints */ 01704 NO_NUM_ST /* Finished, number was malformed */ 01705 } ParseState; 01706 01707 #define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST)) 01708 01709 G_INLINE_FUNC long long int multiplier (int num_decimals); 01710 01711 long long int 01712 multiplier (int num_decimals) 01713 { 01714 switch (num_decimals) 01715 { 01716 case 8: 01717 return 100000000; 01718 case 7: 01719 return 10000000; 01720 case 6: 01721 return 1000000; 01722 case 5: 01723 return 100000; 01724 case 4: 01725 return 10000; 01726 case 3: 01727 return 1000; 01728 case 2: 01729 return 100; 01730 case 1: 01731 return 10; 01732 default: 01733 PERR("bad fraction length"); 01734 g_assert_not_reached(); 01735 break; 01736 } 01737 01738 return 1; 01739 } 01740 01741 gboolean 01742 xaccParseAmount (const char * in_str, gboolean monetary, gnc_numeric *result, 01743 char **endstr) 01744 { 01745 struct lconv *lc = gnc_localeconv(); 01746 01747 gunichar negative_sign; 01748 gunichar decimal_point; 01749 gunichar group_separator; 01750 char *group; 01751 01752 negative_sign = g_utf8_get_char(lc->negative_sign); 01753 if (monetary) 01754 { 01755 group_separator = g_utf8_get_char(lc->mon_thousands_sep); 01756 decimal_point = g_utf8_get_char(lc->mon_decimal_point); 01757 group = lc->mon_grouping; 01758 } 01759 else 01760 { 01761 group_separator = g_utf8_get_char(lc->thousands_sep); 01762 decimal_point = g_utf8_get_char(lc->decimal_point); 01763 group = lc->grouping; 01764 } 01765 01766 return xaccParseAmountExtended(in_str, monetary, negative_sign, decimal_point, 01767 group_separator, group, NULL, result, endstr); 01768 } 01769 01770 /* Note: xaccParseAmountExtended causes test-print-parse-amount 01771 to fail if QOF_SCANF_LLD is simply replaced by G_GINT64_FORMAT. Why? 01772 A: Because scanf and printf use different symbols for 64-bit numbers. 01773 */ 01774 gboolean 01775 xaccParseAmountExtended (const char * in_str, gboolean monetary, 01776 gunichar negative_sign, gunichar decimal_point, 01777 gunichar group_separator, char *group, char *ignore_list, 01778 gnc_numeric *result, char **endstr) 01779 { 01780 gboolean is_negative; 01781 gboolean got_decimal; 01782 gboolean need_paren; 01783 GList * group_data; 01784 long long int numer; 01785 long long int denom; 01786 int count, group_count; 01787 01788 ParseState state; 01789 01790 const gchar *in; 01791 gunichar uc; 01792 gchar *out_str; 01793 gchar *out; 01794 01795 /* Initialize *endstr to in_str */ 01796 if (endstr != NULL) 01797 *endstr = (char *) in_str; 01798 01799 if (in_str == NULL) 01800 return FALSE; 01801 01802 if (!g_utf8_validate(in_str, -1, &in)) 01803 { 01804 printf("Invalid utf8 string '%s'. Bad character at position %ld.\n", 01805 in_str, g_utf8_pointer_to_offset (in_str, in)); 01806 return FALSE; 01807 } 01808 01809 /* 'out_str' will be used to store digits for numeric conversion. 01810 * 'out' will be used to traverse out_str. */ 01811 out = out_str = g_new(gchar, strlen(in_str) + 128); 01812 01813 /* 'in' is used to traverse 'in_str'. */ 01814 in = in_str; 01815 01816 is_negative = FALSE; 01817 got_decimal = FALSE; 01818 need_paren = FALSE; 01819 group_data = NULL; 01820 group_count = 0; 01821 numer = 0; 01822 denom = 1; 01823 01824 /* Initialize the state machine */ 01825 state = START_ST; 01826 01827 /* This while loop implements a state machine for parsing numbers. */ 01828 while (TRUE) 01829 { 01830 ParseState next_state = state; 01831 01832 uc = g_utf8_get_char(in); 01833 01834 /* Ignore anything in the 'ignore list' */ 01835 if (ignore_list && uc && g_utf8_strchr(ignore_list, -1, uc) != NULL) 01836 { 01837 in = g_utf8_next_char(in); 01838 continue; 01839 } 01840 01841 /* Note we never need to check for the end of 'in_str' explicitly. 01842 * The 'else' clauses on all the state transitions will handle that. */ 01843 switch (state) 01844 { 01845 /* START_ST means we have parsed 0 or more whitespace characters */ 01846 case START_ST: 01847 if (g_unichar_isdigit(uc)) 01848 { 01849 count = g_unichar_to_utf8(uc, out); 01850 out += count; /* we record the digits themselves in out_str 01851 * for later conversion by libc routines */ 01852 next_state = PRE_GROUP_ST; 01853 } 01854 else if (uc == decimal_point) 01855 { 01856 next_state = FRAC_ST; 01857 } 01858 else if (g_unichar_isspace(uc)) 01859 { 01860 } 01861 else if (uc == negative_sign) 01862 { 01863 is_negative = TRUE; 01864 next_state = NEG_ST; 01865 } 01866 else if (uc == '(') 01867 { 01868 is_negative = TRUE; 01869 need_paren = TRUE; 01870 next_state = NEG_ST; 01871 } 01872 else 01873 { 01874 next_state = NO_NUM_ST; 01875 } 01876 01877 break; 01878 01879 /* NEG_ST means we have just parsed a negative sign. For now, 01880 * we only recognize formats where the negative sign comes first. */ 01881 case NEG_ST: 01882 if (g_unichar_isdigit(uc)) 01883 { 01884 count = g_unichar_to_utf8(uc, out); 01885 out += count; 01886 next_state = PRE_GROUP_ST; 01887 } 01888 else if (uc == decimal_point) 01889 { 01890 next_state = FRAC_ST; 01891 } 01892 else if (g_unichar_isspace(uc)) 01893 { 01894 } 01895 else 01896 { 01897 next_state = NO_NUM_ST; 01898 } 01899 01900 break; 01901 01902 /* PRE_GROUP_ST means we have started parsing the number, but 01903 * have not encountered a decimal point or a grouping character. */ 01904 case PRE_GROUP_ST: 01905 if (g_unichar_isdigit(uc)) 01906 { 01907 count = g_unichar_to_utf8(uc, out); 01908 out += count; 01909 } 01910 else if (uc == decimal_point) 01911 { 01912 next_state = FRAC_ST; 01913 } 01914 else if (uc == group_separator) 01915 { 01916 next_state = START_GROUP_ST; 01917 } 01918 else if (uc == ')' && need_paren) 01919 { 01920 next_state = DONE_ST; 01921 need_paren = FALSE; 01922 } 01923 else 01924 { 01925 next_state = DONE_ST; 01926 } 01927 01928 break; 01929 01930 /* START_GROUP_ST means we have just parsed a group character. 01931 * Note that group characters might be whitespace!!! In general, 01932 * if a decimal point or a group character is whitespace, we 01933 * try to interpret it in the fashion that will allow parsing 01934 * of the current number to continue. */ 01935 case START_GROUP_ST: 01936 if (g_unichar_isdigit(uc)) 01937 { 01938 count = g_unichar_to_utf8(uc, out); 01939 out += count; 01940 group_count++; /* We record the number of digits 01941 * in the group for later checking. */ 01942 next_state = IN_GROUP_ST; 01943 } 01944 else if (uc == decimal_point) 01945 { 01946 /* If we now get a decimal point, and both the decimal 01947 * and the group separator are also whitespace, assume 01948 * the last group separator was actually whitespace and 01949 * stop parsing. Otherwise, there's a problem. */ 01950 if (g_unichar_isspace(group_separator) && 01951 g_unichar_isspace(decimal_point)) 01952 next_state = DONE_ST; 01953 else 01954 next_state = NO_NUM_ST; 01955 } 01956 else if (uc == ')' && need_paren) 01957 { 01958 if (g_unichar_isspace(group_separator)) 01959 { 01960 next_state = DONE_ST; 01961 need_paren = FALSE; 01962 } 01963 else 01964 next_state = NO_NUM_ST; 01965 } 01966 else 01967 { 01968 /* If the last group separator is also whitespace, 01969 * assume it was intended as such and stop parsing. 01970 * Otherwise, there is a problem. */ 01971 if (g_unichar_isspace(group_separator)) 01972 next_state = DONE_ST; 01973 else 01974 next_state = NO_NUM_ST; 01975 } 01976 break; 01977 01978 /* IN_GROUP_ST means we are in the middle of parsing 01979 * a group of digits. */ 01980 case IN_GROUP_ST: 01981 if (g_unichar_isdigit(uc)) 01982 { 01983 count = g_unichar_to_utf8(uc, out); 01984 out += count; 01985 group_count++; /* We record the number of digits 01986 * in the group for later checking. */ 01987 } 01988 else if (uc == decimal_point) 01989 { 01990 next_state = FRAC_ST; 01991 } 01992 else if (uc == group_separator) 01993 { 01994 next_state = START_GROUP_ST; 01995 } 01996 else if (uc == ')' && need_paren) 01997 { 01998 next_state = DONE_ST; 01999 need_paren = FALSE; 02000 } 02001 else 02002 { 02003 next_state = DONE_ST; 02004 } 02005 02006 break; 02007 02008 /* FRAC_ST means we are now parsing fractional digits. */ 02009 case FRAC_ST: 02010 if (g_unichar_isdigit(uc)) 02011 { 02012 count = g_unichar_to_utf8(uc, out); 02013 out += count; 02014 } 02015 else if (uc == decimal_point) 02016 { 02017 /* If a subsequent decimal point is also whitespace, 02018 * assume it was intended as such and stop parsing. 02019 * Otherwise, there is a problem. */ 02020 if (g_unichar_isspace(decimal_point)) 02021 next_state = DONE_ST; 02022 else 02023 next_state = NO_NUM_ST; 02024 } 02025 else if (uc == group_separator) 02026 { 02027 /* If a subsequent group separator is also whitespace, 02028 * assume it was intended as such and stop parsing. 02029 * Otherwise, there is a problem. */ 02030 if (g_unichar_isspace(group_separator)) 02031 next_state = DONE_ST; 02032 else 02033 next_state = NO_NUM_ST; 02034 } 02035 else if (uc == ')' && need_paren) 02036 { 02037 next_state = DONE_ST; 02038 need_paren = FALSE; 02039 } 02040 else 02041 { 02042 next_state = DONE_ST; 02043 } 02044 02045 break; 02046 02047 default: 02048 PERR("bad state"); 02049 g_assert_not_reached(); 02050 break; 02051 } 02052 02053 /* If we're moving out of the IN_GROUP_ST, record data for the group */ 02054 if ((state == IN_GROUP_ST) && (next_state != IN_GROUP_ST)) 02055 { 02056 group_data = g_list_prepend(group_data, GINT_TO_POINTER(group_count)); 02057 group_count = 0; 02058 } 02059 02060 /* If we're moving into the FRAC_ST or out of the machine 02061 * without going through FRAC_ST, record the integral value. */ 02062 if (((next_state == FRAC_ST) && (state != FRAC_ST)) || 02063 ((next_state == DONE_ST) && !got_decimal)) 02064 { 02065 *out = '\0'; 02066 02067 if (*out_str != '\0' && sscanf(out_str, QOF_SCANF_LLD, &numer) < 1) 02068 { 02069 next_state = NO_NUM_ST; 02070 } 02071 else if (next_state == FRAC_ST) 02072 { 02073 /* reset the out pointer to record the fraction */ 02074 out = out_str; 02075 *out = '\0'; 02076 02077 got_decimal = TRUE; 02078 } 02079 } 02080 02081 state = next_state; 02082 if (done_state (state)) 02083 break; 02084 02085 in = g_utf8_next_char(in); 02086 } 02087 02088 /* If there was an error, just quit */ 02089 if (need_paren || (state == NO_NUM_ST)) 02090 { 02091 g_free(out_str); 02092 g_list_free(group_data); 02093 return FALSE; 02094 } 02095 02096 /* If there were groups, validate them */ 02097 if (group_data != NULL) 02098 { 02099 gboolean good_grouping = TRUE; 02100 GList *node; 02101 02102 /* The groups were built in reverse order. This 02103 * is the easiest order to verify them in. */ 02104 for (node = group_data; group && node; node = node->next) 02105 { 02106 /* Verify group size */ 02107 if (*group != GPOINTER_TO_INT(node->data)) 02108 { 02109 good_grouping = FALSE; 02110 break; 02111 } 02112 02113 /* Peek ahead at the next group code */ 02114 switch (group[1]) 02115 { 02116 /* A null char means repeat the last group indefinitely */ 02117 case '\0': 02118 break; 02119 /* CHAR_MAX means no more grouping allowed */ 02120 case CHAR_MAX: 02121 if (node->next != NULL) 02122 good_grouping = FALSE; 02123 break; 02124 /* Anything else means another group size */ 02125 default: 02126 group++; 02127 break; 02128 } 02129 02130 if (!good_grouping) 02131 break; 02132 } 02133 02134 g_list_free(group_data); 02135 02136 if (!good_grouping) 02137 { 02138 g_free(out_str); 02139 return FALSE; 02140 } 02141 } 02142 02143 /* Cap the end of the fraction string, if any */ 02144 *out = '\0'; 02145 02146 /* Add in fractional value */ 02147 if (got_decimal && (*out_str != '\0')) 02148 { 02149 size_t len; 02150 long long int fraction; 02151 02152 len = strlen(out_str); 02153 02154 if (len > 8) 02155 { 02156 out_str[8] = '\0'; 02157 len = 8; 02158 } 02159 02160 if (sscanf (out_str, QOF_SCANF_LLD, &fraction) < 1) 02161 { 02162 g_free(out_str); 02163 return FALSE; 02164 } 02165 02166 denom = multiplier(len); 02167 numer *= denom; 02168 numer += fraction; 02169 } 02170 else if (monetary && auto_decimal_enabled && !got_decimal) 02171 { 02172 if ((auto_decimal_places > 0) && (auto_decimal_places < 9)) 02173 { 02174 denom = multiplier(auto_decimal_places); 02175 02176 /* No need to multiply numer by denom at this point, 02177 * since by specifying the auto decimal places the 02178 * user has effectively determined the scaling factor 02179 * for the numerator they entered. 02180 */ 02181 } 02182 } 02183 02184 if (result != NULL) 02185 { 02186 *result = gnc_numeric_create (numer, denom); 02187 if (is_negative) 02188 *result = gnc_numeric_neg (*result); 02189 } 02190 02191 if (endstr != NULL) 02192 *endstr = (char *) in; 02193 02194 g_free (out_str); 02195 02196 return TRUE; 02197 } 02198 02199 /* enable/disable the auto_decimal_enabled option */ 02200 static void 02201 gnc_set_auto_decimal_enabled (GConfEntry *entry, gpointer user_data) 02202 { 02203 GConfValue *value; 02204 02205 value = gconf_entry_get_value(entry); 02206 auto_decimal_enabled = gconf_value_get_bool(value); 02207 } 02208 02209 /* set the number of auto decimal places to use */ 02210 static void 02211 gnc_set_auto_decimal_places (GConfEntry *entry, gpointer user_data) 02212 { 02213 GConfValue *value; 02214 02215 value = gconf_entry_get_value(entry); 02216 auto_decimal_places = gconf_value_get_float(value); 02217 } 02218 02219 static void 02220 gnc_auto_decimal_init (void) 02221 { 02222 auto_decimal_enabled = 02223 gnc_gconf_get_bool(GCONF_GENERAL, "auto_decimal_point", NULL); 02224 auto_decimal_places = 02225 (int)gnc_gconf_get_float(GCONF_GENERAL, "auto_decimal_places", NULL); 02226 } 02227 02228 void 02229 gnc_ui_util_init (void) 02230 { 02231 gnc_configure_account_separator (); 02232 gnc_auto_decimal_init(); 02233 02234 gnc_gconf_general_register_cb(KEY_ACCOUNT_SEPARATOR, 02235 (GncGconfGeneralCb)gnc_configure_account_separator, 02236 NULL); 02237 gnc_gconf_general_register_cb(KEY_REVERSED_ACCOUNTS, 02238 (GncGconfGeneralCb)gnc_configure_reverse_balance, 02239 NULL); 02240 gnc_gconf_general_register_cb(KEY_CURRENCY_CHOICE, 02241 gnc_currency_changed_cb, NULL); 02242 gnc_gconf_general_register_cb(KEY_CURRENCY_OTHER, 02243 gnc_currency_changed_cb, NULL); 02244 gnc_gconf_general_register_cb("auto_decimal_point", 02245 gnc_set_auto_decimal_enabled, 02246 NULL); 02247 gnc_gconf_general_register_cb("auto_decimal_places", 02248 gnc_set_auto_decimal_places, 02249 NULL); 02250 02251 }
1.7.4