GnuCash 2.4.99
gnc-ui-util.c
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 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines