1 /********************************************************************\
2  * gnc-ui-util.c -- utility functions for the GnuCash UI            *
3  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com>              *
4  *                                                                  *
5  * This program is free software; you can redistribute it and/or    *
6  * modify it under the terms of the GNU General Public License as   *
7  * published by the Free Software Foundation; either version 2 of   *
8  * the License, or (at your option) any later version.              *
9  *                                                                  *
10  * This program is distributed in the hope that it will be useful,  *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
13  * GNU General Public License for more details.                     *
14  *                                                                  *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact:                        *
17  *                                                                  *
18  * Free Software Foundation           Voice:  +1-617-542-5942       *
19  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
20  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
21 \********************************************************************/
22 
23 #include <config.h>
24 
25 #ifdef __MINGW32__
26 #define __USE_MINGW_ANSI_STDIO 1
27 #endif
28 #include "gnc-ui-util.h"
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32 #include <libguile.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <locale.h>
37 #include <math.h>
38 #if defined(G_OS_WIN32) && !defined(_MSC_VER)
39 # include <pow.h>
40 #endif
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 
46 
47 #include "qof.h"
48 #include "guile-mappings.h"
49 #include "gnc-prefs.h"
50 #include "Account.h"
51 #include "Transaction.h"
52 #include "gnc-engine.h"
53 #include "gnc-features.h"
54 #include "gnc-hooks.h"
55 #include "gnc-locale-tax.h"
56 #include "gnc-session.h"
57 #include "engine-helpers.h"
58 #include "gnc-locale-utils.h"
59 #include "gnc-guile-utils.h"
60 
61 #define GNC_PREF_CURRENCY_CHOICE_LOCALE "currency-choice-locale"
62 #define GNC_PREF_CURRENCY_CHOICE_OTHER  "currency-choice-other"
63 #define GNC_PREF_CURRENCY_OTHER         "currency-other"
64 #define GNC_PREF_REVERSED_ACCTS_NONE    "reversed-accounts-none"
65 #define GNC_PREF_REVERSED_ACCTS_CREDIT  "reversed-accounts-credit"
66 #define GNC_PREF_REVERSED_ACCTS_INC_EXP "reversed-accounts-incomeexpense"
67 #define GNC_PREF_PRICES_FORCE_DECIMAL   "force-price-decimal"
68 
69 static QofLogModule log_module = GNC_MOD_GUI;
70 
71 static gboolean auto_decimal_enabled = FALSE;
72 static int auto_decimal_places = 2;    /* default, can be changed */
73 
74 static gboolean reverse_balance_inited = FALSE;
75 static gboolean reverse_type[NUM_ACCOUNT_TYPES];
76 
77 /* Cache currency ISO codes and only look them up in the settings when
78  * absolutely necessary. Can't cache a pointer to the data structure
79  * as that will change any time the book changes. */
80 static gchar *user_default_currency = NULL;
81 static gchar *user_report_currency = NULL;
82 const static int maximum_decimals = 15;
83 static const gint64 pow_10[] = {1, 10, 100, 1000, 10000, 100000, 1000000,
84                                10000000, 100000000, 1000000000, 10000000000,
85                                100000000000, 1000000000000, 10000000000000,
86                                100000000000000, 1000000000000000};
87 
gnc_normalize_account_separator(const gchar * separator)88 gchar *gnc_normalize_account_separator (const gchar* separator)
89 {
90     gchar *new_sep=NULL;
91 
92     if (!separator || !*separator || g_strcmp0(separator, "colon") == 0)
93             new_sep = g_strdup (":");
94         else if (g_strcmp0(separator, "slash") == 0)
95             new_sep = g_strdup ("/");
96         else if (g_strcmp0(separator, "backslash") == 0)
97             new_sep = g_strdup ("\\");
98         else if (g_strcmp0(separator, "dash") == 0)
99             new_sep = g_strdup ("-");
100         else if (g_strcmp0(separator, "period") == 0)
101             new_sep = g_strdup (".");
102         else
103             new_sep = g_strdup (separator);
104 
105     return new_sep;
106 }
107 /********************************************************************\
108  * gnc_configure_account_separator                                  *
109  *   updates the current account separator character                *
110  *                                                                  *
111  * Args: none                                                       *
112  \*******************************************************************/
113 static void
gnc_configure_account_separator(void)114 gnc_configure_account_separator (void)
115 {
116     gchar *separator;
117     char *string;
118 
119     string = gnc_prefs_get_string(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR);
120     separator = gnc_normalize_account_separator (string);
121 
122     gnc_set_account_separator(separator);
123 
124     g_free(string);
125     g_free(separator);
126 }
127 
128 
129 static void
gnc_configure_reverse_balance(void)130 gnc_configure_reverse_balance (void)
131 {
132     gint i;
133 
134     for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
135         reverse_type[i] = FALSE;
136 
137     if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP))
138     {
139         reverse_type[ACCT_TYPE_INCOME]  = TRUE;
140         reverse_type[ACCT_TYPE_EXPENSE] = TRUE;
141     }
142     else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT))
143     {
144         reverse_type[ACCT_TYPE_LIABILITY] = TRUE;
145         reverse_type[ACCT_TYPE_PAYABLE]   = TRUE;
146         reverse_type[ACCT_TYPE_EQUITY]    = TRUE;
147         reverse_type[ACCT_TYPE_INCOME]    = TRUE;
148         reverse_type[ACCT_TYPE_CREDIT]    = TRUE;
149     }
150     else if (!gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE))
151         PWARN("no reversed account preference set, using none");
152 
153 }
154 
155 static void
gnc_reverse_balance_init(void)156 gnc_reverse_balance_init (void)
157 {
158     gnc_configure_reverse_balance ();
159     reverse_balance_inited = TRUE;
160 }
161 
162 gboolean
gnc_reverse_balance(const Account * account)163 gnc_reverse_balance (const Account *account)
164 {
165     int type;
166 
167     if (account == NULL)
168         return FALSE;
169 
170     type = xaccAccountGetType (account);
171     if ((type < 0) || (type >= NUM_ACCOUNT_TYPES))
172         return FALSE;
173 
174     if (!reverse_balance_inited)
175         gnc_reverse_balance_init ();
176 
177     return reverse_type[type];
178 }
179 
gnc_using_unreversed_budgets(QofBook * book)180 gboolean gnc_using_unreversed_budgets (QofBook* book)
181 {
182     return gnc_features_check_used (book, GNC_FEATURE_BUDGET_UNREVERSED);
183 }
184 
185 /* similar to gnc_reverse_balance but also accepts a gboolean
186    unreversed which specifies the reversal strategy - FALSE = pre-4.x
187    always-assume-credit-accounts, TRUE = all amounts unreversed */
188 gboolean
gnc_reverse_budget_balance(const Account * account,gboolean unreversed)189 gnc_reverse_budget_balance (const Account *account, gboolean unreversed)
190 {
191     if (unreversed == gnc_using_unreversed_budgets(gnc_account_get_book(account)))
192         return gnc_reverse_balance (account);
193 
194     return FALSE;
195 }
196 
gnc_using_equity_type_opening_balance_account(QofBook * book)197 gboolean gnc_using_equity_type_opening_balance_account (QofBook* book)
198 {
199     return gnc_features_check_used (book, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE);
200 }
201 
gnc_set_use_equity_type_opening_balance_account(QofBook * book)202 void gnc_set_use_equity_type_opening_balance_account (QofBook* book)
203 {
204     gnc_features_set_used (book, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE);
205 }
206 
207 gchar *
gnc_get_default_directory(const gchar * section)208 gnc_get_default_directory (const gchar *section)
209 {
210     gchar *dir;
211 
212     dir = gnc_prefs_get_string (section, GNC_PREF_LAST_PATH);
213     if (!dir)
214 #ifdef G_OS_WIN32
215         dir = g_strdup (g_get_user_data_dir ()); /* equivalent of "My Documents" */
216 #else
217         dir = g_strdup (g_get_home_dir ());
218 #endif
219 
220     return dir;
221 }
222 
223 void
gnc_set_default_directory(const gchar * section,const gchar * directory)224 gnc_set_default_directory (const gchar *section, const gchar *directory)
225 {
226     gnc_prefs_set_string(section, GNC_PREF_LAST_PATH, directory);
227 }
228 
229 QofBook *
gnc_get_current_book(void)230 gnc_get_current_book (void)
231 {
232     return qof_session_get_book (gnc_get_current_session ());
233 }
234 
235 /* If there is no current session, there is no book and we must be dealing
236  * with a new book. When gnucash is started with --nofile, there is
237  * initially no session (and no book), but by the time we check, one
238  * could have been created (for example, if an empty account tree tab is
239  * opened, a session is created which creates a new, but empty, book).
240  * A session is created and a book is loaded from a backend when gnucash is
241  * started with a file, but selecting 'new file' keeps a session open. So we
242  * need to check as well for a book with no accounts (root with no children). */
243 gboolean
gnc_is_new_book(void)244 gnc_is_new_book (void)
245 {
246     return ((!gnc_current_session_exist() ||
247                             (gnc_current_session_exist() &&
248                                 !gnc_account_get_children(
249                                     gnc_book_get_root_account(
250                                         gnc_get_current_book()))))
251                             ? TRUE : FALSE);
252 }
253 
254 #define OPTION_TAXUS_NAME "tax_US/name"
255 #define OPTION_TAXUS_TYPE "tax_US/type"
256 #define OLD_OPTION_TAXUS_NAME "book/tax_US/name"
257 #define OLD_OPTION_TAXUS_TYPE "book/tax_US/type"
258 
259 void
gnc_set_current_book_tax_name_type(gboolean name_changed,const gchar * tax_name,gboolean type_changed,const gchar * tax_type)260 gnc_set_current_book_tax_name_type (gboolean name_changed, const gchar *tax_name,
261                                     gboolean type_changed, const gchar *tax_type)
262 {
263     if (name_changed)
264     {
265         if (type_changed)
266         {
267             QofBook* book = gnc_get_current_book();
268             if ((g_strcmp0 (tax_name, "") == 0) ||
269                 (tax_name == NULL))
270             { /* change to no name */
271                 if ((g_strcmp0 (tax_type, "Other") == 0) ||
272                     (g_strcmp0 (tax_type, "") == 0) ||
273                     (tax_type == NULL))
274                 { /* need to delete both name and type and the "tax_US" frame */
275                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, NULL);
276                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, NULL);
277                     qof_book_option_frame_delete(book, "tax_US");
278                 }
279                 else
280                 { /* delete the name & change the type; keep the "tax_US" frame */
281                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, NULL);
282                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
283                 }
284             }
285             else /* new name */
286             {
287                 if ((g_strcmp0 (tax_type, "Other") == 0) ||
288                     (g_strcmp0 (tax_type, "") == 0) ||
289                     (tax_type == NULL))
290                 { /* delete the type & change the name; keep the "tax_US" frame */
291                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, NULL);
292                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
293                 }
294                 else /* and new type */
295                 { /* change the name & change the type */
296                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
297                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
298                 }
299             }
300         }
301         else /* no type change but name changed */
302         {
303             QofBook* book = gnc_get_current_book();
304             if ((g_strcmp0 (tax_name, "") == 0) ||
305                 (tax_name == NULL))
306             { /* change to no name */
307                 if ((g_strcmp0 (tax_type, "Other") == 0) ||
308                     (g_strcmp0 (tax_type, "") == 0) ||
309                     (tax_type == NULL))
310                 { /* delete the name; there is no type; deleted the "tax_US" frame */
311                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, NULL);
312                     qof_book_option_frame_delete(book, "tax_US");
313                 }
314                 else
315                 { /* need to delete the name and keep "tax_US" frame */
316                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, NULL);
317                 }
318             }
319             else
320             { /* change the name & keep "tax_US" frame */
321                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
322             }
323         }
324    }
325    else /* no name change */
326    {
327         if (type_changed)
328         {
329             QofBook* book = gnc_get_current_book();
330             if ((g_strcmp0 (tax_type, "Other") == 0) ||
331                 (g_strcmp0 (tax_type, "") == 0) ||
332                 (tax_type == NULL))
333             {
334                 if ((g_strcmp0 (tax_name, "") == 0) ||
335                     (tax_name == NULL))
336                 {/* delete the type; there is no name; delete the "tax_US" frame */
337                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, NULL);
338                     qof_book_option_frame_delete(book, "tax_US");
339                 }
340                 else
341                 { /* need to delete the type and keep "tax_US" frame */
342                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, NULL);
343                 }
344             }
345             else
346             { /* change the type & keep "tax_US" frame */
347                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
348             }
349         } /*else no name and no type change - do nothing */
350    }
351 }
352 
353 const gchar *
gnc_get_current_book_tax_name(void)354 gnc_get_current_book_tax_name (void)
355 {
356     QofBook* book = gnc_get_current_book();
357     const char* tax_name =
358         qof_book_get_string_option(book, OPTION_TAXUS_NAME);
359     if (tax_name)
360     {
361         return tax_name;
362     }
363     else
364     {
365         const char* old_option_taxus_name =
366             qof_book_get_string_option(book, OLD_OPTION_TAXUS_NAME);
367         if (old_option_taxus_name)
368         {
369             char* taxus_name = g_strdup(old_option_taxus_name);
370             const char* old_option_taxus_type =
371                 qof_book_get_string_option(book, OLD_OPTION_TAXUS_TYPE);
372             if (old_option_taxus_type)
373             { /* switch both name and type and remove unused frames */
374                 char* taxus_type = g_strdup(old_option_taxus_type);
375                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
376                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, NULL);
377                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
378                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, NULL);
379                 qof_book_option_frame_delete(book, "book/tax_US");
380                 qof_book_option_frame_delete(book, "book");
381                 g_free (taxus_type);
382             }
383             else
384             { /* switch just name and remove unused frames */
385                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
386                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, NULL);
387                 qof_book_option_frame_delete(book, "book/tax_US");
388                 qof_book_option_frame_delete(book, "book");
389             }
390             g_free (taxus_name);
391             return qof_book_get_string_option(book, OPTION_TAXUS_NAME);
392         }
393         return NULL;
394     }
395 }
396 
397 const gchar *
gnc_get_current_book_tax_type(void)398 gnc_get_current_book_tax_type (void)
399 {
400     QofBook* book = gnc_get_current_book();
401     const char* tax_type =
402         qof_book_get_string_option(book, OPTION_TAXUS_TYPE);
403     if (tax_type)
404     {
405         return tax_type;
406     }
407     else
408     {
409         const char* old_option_taxus_type =
410             qof_book_get_string_option(book, OLD_OPTION_TAXUS_TYPE);
411         if (old_option_taxus_type)
412         {
413             char* taxus_type = g_strdup(old_option_taxus_type);
414             const char* old_option_taxus_name =
415                 qof_book_get_string_option(book, OLD_OPTION_TAXUS_NAME);
416             if (old_option_taxus_name)
417             { /* switch both name and type and remove unused frames */
418                 char* taxus_name = g_strdup(old_option_taxus_name);
419                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
420                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, NULL);
421                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
422                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, NULL);
423                 qof_book_option_frame_delete(book, "book/tax_US");
424                 qof_book_option_frame_delete(book, "book");
425                 g_free (taxus_name);
426             }
427             else
428             { /* switch just type and remove unused frames */
429                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
430                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, NULL);
431                 qof_book_option_frame_delete(book, "book/tax_US");
432                 qof_book_option_frame_delete(book, "book");
433             }
434             g_free (taxus_type);
435             return qof_book_get_string_option(book, OPTION_TAXUS_TYPE);
436         }
437         return NULL;
438     }
439 }
440 
441 /** Returns TRUE if both book-currency and default gain/loss policy KVPs exist
442   * and are valid and trading accounts are not used. */
443 gboolean
gnc_book_use_book_currency(QofBook * book)444 gnc_book_use_book_currency (QofBook *book)
445 {
446     const gchar *policy;
447     const gchar *currency;
448 
449     if (!book) return FALSE;
450 
451     policy = qof_book_get_default_gains_policy (book);
452     currency = qof_book_get_book_currency_name (book);
453 
454     /* If either a default gain/loss policy or a book-currency does not exist,
455        book-currency accounting method not valid */
456     if (!policy || !currency)
457        return FALSE;
458 
459     /* If both exist, both must be valid */
460     if (!gnc_valid_policy_name (policy) || !gnc_commodity_table_lookup
461                                             (gnc_commodity_table_get_table
462                                               (gnc_get_current_book()),
463                                                 GNC_COMMODITY_NS_CURRENCY,
464                                                  currency))
465        return FALSE;
466 
467     /* If both exist and are valid, there must be no trading accounts flag */
468     if (qof_book_use_trading_accounts (book))
469        return FALSE;
470 
471     return TRUE;
472 }
473 
474 /** Returns pointer to Book Currency name for book or NULL; determines
475   * that both book-currency and default gain/loss policy KVPs exist and that
476   * both are valid, a requirement for the 'book-currency' currency accounting
477   * method to apply. */
478 const gchar *
gnc_book_get_book_currency_name(QofBook * book)479 gnc_book_get_book_currency_name (QofBook *book)
480 {
481     if (!book) return NULL;
482 
483     if (gnc_book_use_book_currency (book))
484         return qof_book_get_book_currency_name (book);
485 
486     return NULL;
487 }
488 
489 /** Returns pointer to Book Currency for book or NULL; determines
490   * that both book-currency and default gain/loss policy KVPs exist and that
491   * both are valid, a requirement for the 'book-currency' currency accounting
492   * method to apply. */
493 gnc_commodity *
gnc_book_get_book_currency(QofBook * book)494 gnc_book_get_book_currency (QofBook *book)
495 {
496     if (!book) return NULL;
497 
498     if (gnc_book_use_book_currency (book))
499         return gnc_commodity_table_lookup
500                     (gnc_commodity_table_get_table(book),
501                      GNC_COMMODITY_NS_CURRENCY,
502                      qof_book_get_book_currency_name (book));
503 
504     return NULL;
505 }
506 
507 /** Returns pointer to default gain/loss policy for book or NULL; determines
508   * that both book-currency and default gain/loss policy KVPs exist and that
509   * both are valid, a requirement for the 'book-currency' currency accounting
510   * method to apply. */
511 const gchar *
gnc_book_get_default_gains_policy(QofBook * book)512 gnc_book_get_default_gains_policy (QofBook *book)
513 {
514     if (!book) return NULL;
515 
516     if (gnc_book_use_book_currency (book))
517         return qof_book_get_default_gains_policy (book);
518 
519     return NULL;
520 }
521 
522 /** Returns pointer to default gain/loss account for book or NULL; determines
523   * that both book-currency and default gain/loss policy KVPs exist and that
524   * both are valid, a requirement for the 'book-currency' currency accounting
525   * method to apply. Also, account must not be hidden or a placeholder, and
526   * must be of same currency as book-currency and income or expense type */
527 Account *
gnc_book_get_default_gain_loss_acct(QofBook * book)528 gnc_book_get_default_gain_loss_acct (QofBook *book)
529 {
530     Account *gains_account = NULL;
531 
532     if (!book) return NULL;
533 
534     if (gnc_book_use_book_currency (book))
535     {
536         GncGUID *guid = qof_book_get_default_gain_loss_acct_guid (book);
537         gains_account = xaccAccountLookup (guid, book);
538         guid_free (guid);
539     }
540 
541     if (gains_account &&
542         !xaccAccountGetPlaceholder(gains_account) &&
543         !xaccAccountGetHidden(gains_account) &&
544         (gnc_commodity_equal(xaccAccountGetCommodity(gains_account),
545                                     gnc_book_get_book_currency(book))) &&
546         ((xaccAccountGetType(gains_account) == ACCT_TYPE_INCOME) ||
547             (xaccAccountGetType(gains_account) == ACCT_TYPE_EXPENSE)))
548     {
549         return gains_account;
550     }
551     else
552     {
553         return NULL;
554     }
555 }
556 
557 Account *
gnc_get_current_root_account(void)558 gnc_get_current_root_account (void)
559 {
560     return gnc_book_get_root_account (gnc_get_current_book ());
561 }
562 
563 gnc_commodity_table *
gnc_get_current_commodities(void)564 gnc_get_current_commodities (void)
565 {
566     return gnc_commodity_table_get_table (gnc_get_current_book ());
567 }
568 
569 gchar *
gnc_get_account_name_for_split_register(const Account * account,gboolean show_leaf_accounts)570 gnc_get_account_name_for_split_register(const Account *account, gboolean show_leaf_accounts)
571 {
572     if (show_leaf_accounts)
573         return g_strdup (xaccAccountGetName (account));
574     else
575         return gnc_account_get_full_name (account);
576 }
577 
578 gchar *
gnc_get_account_name_for_register(const Account * account)579 gnc_get_account_name_for_register(const Account *account)
580 {
581     gboolean show_leaf_accounts;
582     show_leaf_accounts = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
583                                             GNC_PREF_SHOW_LEAF_ACCT_NAMES);
584 
585     return gnc_get_account_name_for_split_register(account, show_leaf_accounts);
586 }
587 
588 Account *
gnc_account_lookup_for_register(const Account * base_account,const char * name)589 gnc_account_lookup_for_register(const Account *base_account, const char *name)
590 {
591     gboolean show_leaf_accounts;
592     show_leaf_accounts = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
593                                             GNC_PREF_SHOW_LEAF_ACCT_NAMES);
594 
595     if (show_leaf_accounts)
596         return gnc_account_lookup_by_name (base_account, name);
597     else
598         return gnc_account_lookup_by_full_name (base_account, name);
599 }
600 
601 
602 /* Caller is responsible for g_free'ing returned memory */
603 char *
gnc_ui_account_get_tax_info_string(const Account * account)604 gnc_ui_account_get_tax_info_string (const Account *account)
605 {
606     static SCM get_form = SCM_UNDEFINED;
607     static SCM get_desc = SCM_UNDEFINED;
608 
609     gboolean tax_related = FALSE;
610     const char *code;
611 
612     if (!account)
613         return NULL;
614 
615     tax_related = xaccAccountGetTaxRelated (account);
616     code = xaccAccountGetTaxUSCode (account);
617 
618     if (!code)
619     {
620         if (!tax_related)
621             return NULL;
622         /* tax_related && !code */
623         else
624             /* Translators: This and the following strings appear on
625                the account tab if the Tax Info column is displayed,
626                i.e. if the user wants to record the tax form number
627                and location on that tax form which corresponds to this
628                gnucash account. For the US Income Tax support in
629                gnucash, each tax code that can be assigned to an
630                account generally corresponds to a specific line number
631                on a paper form and each form has a unique
632                identification (e.g., Form 1040, Schedule A). */
633             return g_strdup (_("Tax-related but has no tax code"));
634     }
635     else  /* with tax code */
636     {
637         const gchar *tax_type;
638         GNCAccountType atype;
639         SCM tax_entity_type;
640         SCM category;
641         gchar *num_code = NULL;
642         const gchar *prefix = "N";
643         gchar *return_string = NULL;
644 
645         tax_type = gnc_get_current_book_tax_type ();
646         if (tax_type == NULL || (g_strcmp0 (tax_type, "") == 0))
647             return g_strdup (_("Tax entity type not specified"));
648 
649         atype = xaccAccountGetType (account);
650         tax_entity_type = scm_from_utf8_string (tax_type);
651 
652         if (get_form == SCM_UNDEFINED)
653         {
654             const gchar *tax_module;
655             /* load the tax info */
656             gnc_locale_tax_init ();
657 
658             get_form = scm_c_eval_string
659                        ("(false-if-exception gnc:txf-get-form)");
660             get_desc = scm_c_eval_string
661                        ("(false-if-exception gnc:txf-get-description)");
662         }
663 
664         g_return_val_if_fail (scm_is_procedure (get_form), NULL);
665         g_return_val_if_fail (scm_is_procedure (get_desc), NULL);
666 
667         category = scm_c_eval_string (atype == ACCT_TYPE_INCOME ?
668                                       "txf-income-categories" :
669                                       (atype == ACCT_TYPE_EXPENSE ?
670                                        "txf-expense-categories" :
671                                        (((atype == ACCT_TYPE_BANK)      ||
672                                          (atype == ACCT_TYPE_CASH)      ||
673                                          (atype == ACCT_TYPE_ASSET)     ||
674                                          (atype == ACCT_TYPE_STOCK)     ||
675                                          (atype == ACCT_TYPE_MUTUAL)    ||
676                                          (atype == ACCT_TYPE_RECEIVABLE)) ?
677                                         "txf-asset-categories" :
678                                         (((atype == ACCT_TYPE_CREDIT)    ||
679                                           (atype == ACCT_TYPE_LIABILITY) ||
680                                           (atype == ACCT_TYPE_EQUITY)    ||
681                                           (atype == ACCT_TYPE_PAYABLE)) ?
682                                          "txf-liab-eq-categories" : ""))));
683 
684         if (g_str_has_prefix (code, prefix))
685         {
686             const gchar *num_code_tmp;
687             num_code_tmp = g_strdup (code);
688             num_code_tmp++; /* to lose the leading N */
689             num_code = g_strdup (num_code_tmp);
690             num_code_tmp--;
691             g_free ((gpointer *) num_code_tmp);
692         }
693         else
694         {
695             num_code = g_strdup (code);
696         }
697 
698         if (category == SCM_UNDEFINED)
699         {
700             if (tax_related)
701                 return_string = g_strdup_printf
702                                 (_("Tax type %s: invalid code %s for account type"),
703                                  tax_type, num_code);
704             else
705                 return_string = g_strdup_printf
706                                 (_("Not tax-related; tax type %s: invalid code %s for account type"),
707                                  tax_type, num_code);
708         }
709         else
710         {
711             SCM code_scm;
712             SCM form_scm;
713             code_scm = scm_from_locale_symbol (code);
714             form_scm = scm_call_3 (get_form, category, code_scm, tax_entity_type);
715             if (!scm_is_string (form_scm))
716             {
717                 if (tax_related)
718                     return_string =  g_strdup_printf
719                                      (_("Invalid code %s for tax type %s"),
720                                       num_code, tax_type);
721                 else
722                     return_string =  g_strdup_printf
723                                      (_("Not tax-related; invalid code %s for tax type %s"),
724                                       num_code, tax_type);
725             }
726             else
727             {
728                 gchar *form = NULL;
729 
730                 /* Note: using scm_to_utf8_stringn directly here instead
731                    of our wrapper gnc_scm_to_utf8_string. 'form' should
732                    be freed with 'free' instead of 'g_free'. This will
733                    be taken care of automatically during scm_dynwind_end,
734                    because we inform guile of this memory allocation via
735                    scm_dynwind_free a little further. */
736                 form = scm_to_utf8_stringn (form_scm, NULL);
737                 if (!form)
738                 {
739                     if (tax_related)
740                         return_string = g_strdup_printf
741                                         (_("No form: code %s, tax type %s"), num_code,
742                                          tax_type);
743                     else
744                         return_string = g_strdup_printf
745                                         (_("Not tax-related; no form: code %s, tax type %s"),
746                                          num_code, tax_type);
747                 }
748                 else
749                 {
750                     SCM desc_scm;
751 
752                     /* Create a dynwind context because we will be calling (scm) functions
753                        that potentially exit non-locally */
754                     scm_dynwind_begin (0);
755                     scm_dynwind_free (form);
756                     desc_scm = scm_call_3 (get_desc, category, code_scm,
757                                            tax_entity_type);
758                     if (!scm_is_string (desc_scm))
759                     {
760                         if (tax_related)
761                             return_string = g_strdup_printf
762                                             (_("No description: form %s, code %s, tax type %s"),
763                                              form, num_code, tax_type);
764                         else
765                             return_string = g_strdup_printf
766                                             (_("Not tax-related; no description: form %s, code %s, tax type %s"),
767                                              form, num_code, tax_type);
768                     }
769                     else
770                     {
771                         gchar *desc = NULL;
772                         desc = gnc_scm_to_utf8_string (desc_scm);
773                         if (!desc)
774                         {
775                             if (tax_related)
776                                 return_string = g_strdup_printf
777                                                 (_("No description: form %s, code %s, tax type %s"),
778                                                  form, num_code, tax_type);
779                             else
780                                 return_string = g_strdup_printf
781                                                 (_("Not tax-related; no description: form %s, code %s, tax type %s"),
782                                                  form, num_code, tax_type);
783                         }
784                         else
785                         {
786                             gint64 copy_number;
787                             gchar *copy_txt = NULL;
788                             copy_number = xaccAccountGetTaxUSCopyNumber (account);
789                             copy_txt = (copy_number == 1) ?
790                                        g_strdup ("") :
791                                        g_strdup_printf ("(%d)",
792                                                         (gint) copy_number);
793                             if (tax_related)
794                             {
795                                 if (g_strcmp0 (form, "") == 0)
796                                     return_string = g_strdup_printf ("%s", desc);
797                                 else
798                                     return_string = g_strdup_printf ("%s%s: %s",
799                                                                      form, copy_txt, desc);
800                             }
801                             else
802                             {
803                                 return_string = g_strdup_printf
804                                                 (_("Not tax-related; %s%s: %s (code %s, tax type %s)"),
805                                                  form, copy_txt, desc, num_code, tax_type);
806                             }
807                             g_free (copy_txt);
808                         }
809                         g_free (desc);
810                     }
811                     scm_dynwind_end ();
812                 }
813             }
814         }
815         g_free (num_code);
816         return return_string;
817     }
818 }
819 
820 /* Caller is responsible for g_free'ing returned memory */
821 char *
gnc_ui_account_get_tax_info_sub_acct_string(const Account * account)822 gnc_ui_account_get_tax_info_sub_acct_string (const Account *account)
823 {
824     GList *descendant, *account_descendants;
825 
826     if (!account)
827         return NULL;
828 
829     account_descendants = gnc_account_get_descendants (account);
830     if (account_descendants)
831     {
832         gint sub_acct_tax_number = 0;
833         for (descendant = account_descendants; descendant;
834                 descendant = g_list_next(descendant))
835         {
836             if (xaccAccountGetTaxRelated (descendant->data))
837                 sub_acct_tax_number++;
838         }
839         g_list_free (account_descendants);
840         g_list_free (descendant);
841         /* Translators: This and the following strings appear on
842            the account tab if the Tax Info column is displayed,
843            i.e. if the user wants to record the tax form number
844            and location on that tax form which corresponds to this
845            gnucash account. For the US Income Tax support in
846            gnucash, each tax code that can be assigned to an
847            account generally corresponds to a specific line number
848            on a paper form and each form has a unique
849            identification (e.g., Form 1040, Schedule A). */
850         return (sub_acct_tax_number == 0) ? NULL :
851                g_strdup_printf (_("(Tax-related subaccounts: %d)"),
852                                 sub_acct_tax_number);
853     }
854     else
855         return NULL;
856 }
857 
858 /********************************************************************\
859  * gnc_get_reconcile_str                                            *
860  *   return the i18n'd string for the given reconciled flag         *
861  *                                                                  *
862  * Args: reconciled_flag - the flag to convert into a string        *
863  * Returns: the i18n'd reconciled string                            *
864 \********************************************************************/
865 const char *
gnc_get_reconcile_str(char reconciled_flag)866 gnc_get_reconcile_str (char reconciled_flag)
867 {
868     switch (reconciled_flag)
869     {
870     case NREC:
871         return C_("Reconciled flag 'not cleared'", "n");
872     case CREC:
873         return C_("Reconciled flag 'cleared'", "c");
874     case YREC:
875         return C_("Reconciled flag 'reconciled'", "y");
876     case FREC:
877         return C_("Reconciled flag 'frozen'", "f");
878     case VREC:
879         return C_("Reconciled flag 'void'", "v");
880     default:
881         PERR("Bad reconciled flag\n");
882         return NULL;
883     }
884 }
885 
886 /********************************************************************\
887  * gnc_get_reconcile_valid_flags                                    *
888  *   return a string containing the list of reconciled flags        *
889  *                                                                  *
890  * Returns: the i18n'd reconciled flags string                      *
891 \********************************************************************/
892 const char *
gnc_get_reconcile_valid_flags(void)893 gnc_get_reconcile_valid_flags (void)
894 {
895     static const char flags[] = { NREC, CREC, YREC, FREC, VREC, 0 };
896     return flags;
897 }
898 
899 /********************************************************************\
900  * gnc_get_reconcile_flag_order                                     *
901  *   return a string containing the reconciled-flag change order    *
902  *                                                                  *
903  * Args: reconciled_flag - the flag to convert into a string        *
904  * Returns: the i18n'd reconciled string                            *
905 \********************************************************************/
906 const char *
gnc_get_reconcile_flag_order(void)907 gnc_get_reconcile_flag_order (void)
908 {
909     static const char flags[] = { NREC, CREC, 0 };
910     return flags;
911 }
912 
913 const char *
gnc_get_doclink_str(char link_flag)914 gnc_get_doclink_str (char link_flag)
915 {
916     switch (link_flag)
917     {
918     case WLINK:
919         return C_("Document Link flag for 'web'", "w");
920     case FLINK:
921         return C_("Document Link flag for 'file'", "f");
922     case ' ':
923         return " ";
924     default:
925         PERR("Bad link flag");
926         return NULL;
927     }
928 }
929 
930 const char *
gnc_get_doclink_valid_flags(void)931 gnc_get_doclink_valid_flags (void)
932 {
933     static const char flags[] = { FLINK, WLINK, ' ', 0 };
934     return flags;
935 }
936 
937 const char *
gnc_get_doclink_flag_order(void)938 gnc_get_doclink_flag_order (void)
939 {
940     static const char flags[] = { FLINK, WLINK, ' ', 0 };
941     return flags;
942 }
943 
944 static const char *
equity_base_name(GNCEquityType equity_type)945 equity_base_name (GNCEquityType equity_type)
946 {
947     switch (equity_type)
948     {
949     case EQUITY_OPENING_BALANCE:
950         return N_("Opening Balances");
951 
952     case EQUITY_RETAINED_EARNINGS:
953         return N_("Retained Earnings");
954 
955     default:
956         return NULL;
957     }
958 }
959 
960 Account *
gnc_find_or_create_equity_account(Account * root,GNCEquityType equity_type,gnc_commodity * currency)961 gnc_find_or_create_equity_account (Account *root,
962                                    GNCEquityType equity_type,
963                                    gnc_commodity *currency)
964 {
965     Account *parent;
966     Account *account = NULL;
967     gboolean name_exists;
968     gboolean base_name_exists;
969     const char *base_name;
970     char *name;
971     gboolean use_eq_op_feature;
972 
973     g_return_val_if_fail (equity_type >= 0, NULL);
974     g_return_val_if_fail (equity_type < NUM_EQUITY_TYPES, NULL);
975     g_return_val_if_fail (currency != NULL, NULL);
976     g_return_val_if_fail (root != NULL, NULL);
977     g_return_val_if_fail (gnc_commodity_is_currency(currency), NULL);
978 
979     use_eq_op_feature = equity_type == EQUITY_OPENING_BALANCE && gnc_using_equity_type_opening_balance_account (gnc_get_current_book ());
980 
981     if (use_eq_op_feature)
982     {
983         account = gnc_account_lookup_by_opening_balance (root, currency);
984         if (account)
985             return account;
986     }
987 
988     base_name = equity_base_name (equity_type);
989 
990     account = gnc_account_lookup_by_name(root, base_name);
991     if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
992         account = NULL;
993 
994     if (!account)
995     {
996         base_name = base_name && *base_name ? _(base_name) : "";
997 
998         account = gnc_account_lookup_by_name(root, base_name);
999         if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
1000             account = NULL;
1001     }
1002 
1003     base_name_exists = (account != NULL);
1004 
1005     if (account &&
1006             gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
1007     {
1008         if (use_eq_op_feature)
1009             xaccAccountSetIsOpeningBalance (account, TRUE);
1010         return account;
1011     }
1012 
1013     name = g_strconcat (base_name, " - ",
1014                         gnc_commodity_get_mnemonic (currency), NULL);
1015     account = gnc_account_lookup_by_name(root, name);
1016     if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
1017         account = NULL;
1018 
1019     name_exists = (account != NULL);
1020 
1021     if (account &&
1022             gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
1023     {
1024         if (use_eq_op_feature)
1025             xaccAccountSetIsOpeningBalance (account, TRUE);
1026         return account;
1027     }
1028 
1029     /* Couldn't find one, so create it */
1030     if (name_exists && base_name_exists)
1031     {
1032         PWARN ("equity account with unexpected currency");
1033         g_free (name);
1034         return NULL;
1035     }
1036 
1037     if (!base_name_exists &&
1038             gnc_commodity_equiv (currency, gnc_default_currency ()))
1039     {
1040         g_free (name);
1041         name = g_strdup (base_name);
1042     }
1043 
1044     parent = gnc_account_lookup_by_name(root, _("Equity"));
1045     if (!parent || xaccAccountGetType (parent) != ACCT_TYPE_EQUITY)
1046         parent = root;
1047     g_assert(parent);
1048 
1049     account = xaccMallocAccount (gnc_account_get_book(root));
1050 
1051     xaccAccountBeginEdit (account);
1052 
1053     xaccAccountSetName (account, name);
1054     xaccAccountSetType (account, ACCT_TYPE_EQUITY);
1055     xaccAccountSetCommodity (account, currency);
1056 
1057     if (use_eq_op_feature)
1058         xaccAccountSetIsOpeningBalance (account, TRUE);
1059 
1060     xaccAccountBeginEdit (parent);
1061     gnc_account_append_child (parent, account);
1062     xaccAccountCommitEdit (parent);
1063 
1064     xaccAccountCommitEdit (account);
1065 
1066     g_free (name);
1067 
1068     return account;
1069 }
1070 
1071 gboolean
gnc_account_create_opening_balance(Account * account,gnc_numeric balance,time64 date,QofBook * book)1072 gnc_account_create_opening_balance (Account *account,
1073                                     gnc_numeric balance,
1074                                     time64 date,
1075                                     QofBook *book)
1076 {
1077     Account *equity_account;
1078     Transaction *trans;
1079     Split *split;
1080     gnc_commodity *commodity;
1081 
1082     if (gnc_numeric_zero_p (balance))
1083         return TRUE;
1084 
1085     g_return_val_if_fail (account != NULL, FALSE);
1086     commodity = xaccAccountGetCommodity (account);
1087     g_return_val_if_fail (gnc_commodity_is_currency (commodity), FALSE);
1088 
1089     equity_account =
1090         gnc_find_or_create_equity_account (gnc_account_get_root(account),
1091                                            EQUITY_OPENING_BALANCE,
1092                                            commodity);
1093     if (!equity_account)
1094         return FALSE;
1095 
1096     xaccAccountBeginEdit (account);
1097     xaccAccountBeginEdit (equity_account);
1098 
1099     trans = xaccMallocTransaction (book);
1100 
1101     xaccTransBeginEdit (trans);
1102 
1103     xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL));
1104     xaccTransSetDatePostedSecsNormalized (trans, date);
1105     xaccTransSetDescription (trans, _("Opening Balance"));
1106 
1107     split = xaccMallocSplit (book);
1108 
1109     xaccTransAppendSplit (trans, split);
1110     xaccAccountInsertSplit (account, split);
1111 
1112     xaccSplitSetAmount (split, balance);
1113     xaccSplitSetValue (split, balance);
1114 
1115     balance = gnc_numeric_neg (balance);
1116 
1117     split = xaccMallocSplit (book);
1118 
1119     xaccTransAppendSplit (trans, split);
1120     xaccAccountInsertSplit (equity_account, split);
1121 
1122     xaccSplitSetAmount (split, balance);
1123     xaccSplitSetValue (split, balance);
1124 
1125     xaccTransCommitEdit (trans);
1126     xaccAccountCommitEdit (equity_account);
1127     xaccAccountCommitEdit (account);
1128 
1129     return TRUE;
1130 }
1131 
1132 
1133 gnc_commodity *
gnc_locale_default_currency_nodefault(void)1134 gnc_locale_default_currency_nodefault (void)
1135 {
1136     gnc_commodity * currency;
1137     gnc_commodity_table *table;
1138     const char *code;
1139 
1140     table = gnc_get_current_commodities ();
1141     code = gnc_locale_default_iso_currency_code ();
1142 
1143     currency = gnc_commodity_table_lookup (table, GNC_COMMODITY_NS_CURRENCY, code);
1144 
1145     return (currency ? currency : NULL);
1146 }
1147 
1148 gnc_commodity *
gnc_locale_default_currency(void)1149 gnc_locale_default_currency (void)
1150 {
1151     gnc_commodity * currency = gnc_locale_default_currency_nodefault ();
1152 
1153     return (currency ? currency :
1154             gnc_commodity_table_lookup (gnc_get_current_commodities (),
1155                                         GNC_COMMODITY_NS_CURRENCY, "USD"));
1156 }
1157 
1158 
1159 static gnc_commodity *
gnc_default_currency_common(gchar * requested_currency,const gchar * section)1160 gnc_default_currency_common (gchar *requested_currency,
1161                              const gchar *section)
1162 {
1163     gnc_commodity *currency = NULL;
1164     gchar  *mnemonic;
1165 
1166     if (requested_currency)
1167         return gnc_commodity_table_lookup(gnc_get_current_commodities(),
1168                                           GNC_COMMODITY_NS_CURRENCY,
1169                                           requested_currency);
1170 
1171     if (gnc_book_use_book_currency (gnc_get_current_book ()))
1172         return gnc_book_get_book_currency (gnc_get_current_book ());
1173 
1174     if (gnc_prefs_get_bool (section, GNC_PREF_CURRENCY_CHOICE_OTHER))
1175     {
1176         mnemonic = gnc_prefs_get_string(section, GNC_PREF_CURRENCY_OTHER);
1177         currency = gnc_commodity_table_lookup(gnc_get_current_commodities(),
1178                                               GNC_COMMODITY_NS_CURRENCY, mnemonic);
1179         DEBUG("mnemonic %s, result %p", mnemonic ? mnemonic : "(null)", currency);
1180         g_free(mnemonic);
1181     }
1182 
1183     if (!currency)
1184         currency = gnc_locale_default_currency ();
1185     if (currency)
1186     {
1187         mnemonic = requested_currency;
1188         g_free(mnemonic);
1189     }
1190     return currency;
1191 }
1192 
1193 gnc_commodity *
gnc_default_currency(void)1194 gnc_default_currency (void)
1195 {
1196     return gnc_default_currency_common (user_default_currency, GNC_PREFS_GROUP_GENERAL);
1197 }
1198 
gnc_account_or_default_currency(const Account * account,gboolean * currency_from_account_found)1199 gnc_commodity * gnc_account_or_default_currency(const Account* account, gboolean * currency_from_account_found)
1200 {
1201     gnc_commodity *currency;
1202     if (!account)
1203     {
1204         if (currency_from_account_found)
1205             *currency_from_account_found = FALSE;
1206         return gnc_default_currency();
1207     }
1208 
1209     currency = gnc_account_get_currency_or_parent(account);
1210     if (currency)
1211     {
1212         if (currency_from_account_found)
1213             *currency_from_account_found = TRUE;
1214     }
1215     else
1216     {
1217         if (currency_from_account_found)
1218             *currency_from_account_found = FALSE;
1219         currency = gnc_default_currency();
1220     }
1221     return currency;
1222 }
1223 
1224 
1225 
1226 gnc_commodity *
gnc_default_report_currency(void)1227 gnc_default_report_currency (void)
1228 {
1229     return gnc_default_currency_common (user_report_currency, GNC_PREFS_GROUP_GENERAL_REPORT);
1230 }
1231 
1232 static void
gnc_currency_changed_cb(GSettings * settings,gchar * key,gpointer user_data)1233 gnc_currency_changed_cb (GSettings *settings, gchar *key, gpointer user_data)
1234 {
1235     user_default_currency = NULL;
1236     user_report_currency = NULL;
1237     gnc_hook_run(HOOK_CURRENCY_CHANGED, NULL);
1238 }
1239 
1240 
1241 GNCPrintAmountInfo
gnc_default_print_info(gboolean use_symbol)1242 gnc_default_print_info (gboolean use_symbol)
1243 {
1244     static GNCPrintAmountInfo info;
1245     static gboolean got_it = FALSE;
1246     struct lconv *lc;
1247 
1248     /* These must be updated each time. */
1249     info.use_symbol = use_symbol ? 1 : 0;
1250     info.commodity = gnc_default_currency ();
1251 
1252     if (got_it)
1253         return info;
1254 
1255     lc = gnc_localeconv ();
1256 
1257     info.max_decimal_places = lc->frac_digits;
1258     info.min_decimal_places = lc->frac_digits;
1259 
1260     info.use_separators = 1;
1261     info.use_locale = 1;
1262     info.monetary = 1;
1263     info.force_fit = 0;
1264     info.round = 0;
1265 
1266     got_it = TRUE;
1267 
1268     return info;
1269 }
1270 
1271 static gboolean
is_decimal_fraction(int fraction,guint8 * max_decimal_places_p)1272 is_decimal_fraction (int fraction, guint8 *max_decimal_places_p)
1273 {
1274     guint8 max_decimal_places = 0;
1275 
1276     if (fraction <= 0)
1277         return FALSE;
1278 
1279     while (fraction != 1)
1280     {
1281         if (fraction % 10 != 0)
1282             return FALSE;
1283 
1284         fraction = fraction / 10;
1285         max_decimal_places += 1;
1286     }
1287 
1288     if (max_decimal_places_p)
1289         *max_decimal_places_p = max_decimal_places;
1290 
1291     return TRUE;
1292 }
1293 
1294 GNCPrintAmountInfo
gnc_commodity_print_info(const gnc_commodity * commodity,gboolean use_symbol)1295 gnc_commodity_print_info (const gnc_commodity *commodity,
1296                           gboolean use_symbol)
1297 {
1298     GNCPrintAmountInfo info;
1299     gboolean is_iso;
1300 
1301     if (commodity == NULL)
1302         return gnc_default_print_info (use_symbol);
1303 
1304     info.commodity = commodity;
1305 
1306     is_iso = gnc_commodity_is_iso (commodity);
1307 
1308     if (is_decimal_fraction (gnc_commodity_get_fraction (commodity),
1309                              &info.max_decimal_places))
1310     {
1311         if (is_iso)
1312             info.min_decimal_places = info.max_decimal_places;
1313         else
1314             info.min_decimal_places = 0;
1315     }
1316     else
1317         info.max_decimal_places = info.min_decimal_places = 0;
1318 
1319     info.use_separators = 1;
1320     info.use_symbol = use_symbol ? 1 : 0;
1321     info.use_locale = is_iso ? 1 : 0;
1322     info.monetary = 1;
1323     info.force_fit = 0;
1324     info.round = 0;
1325 
1326     return info;
1327 }
1328 
1329 static GNCPrintAmountInfo
gnc_account_print_info_helper(const Account * account,gboolean use_symbol,gnc_commodity * (* efffunc)(const Account *),int (* scufunc)(const Account *))1330 gnc_account_print_info_helper(const Account *account, gboolean use_symbol,
1331                               gnc_commodity * (*efffunc)(const Account *),
1332                               int (*scufunc)(const Account*))
1333 {
1334     GNCPrintAmountInfo info;
1335     gboolean is_iso;
1336     int scu;
1337 
1338     if (account == NULL)
1339         return gnc_default_print_info (use_symbol);
1340 
1341     info.commodity = efffunc (account);
1342 
1343     is_iso = gnc_commodity_is_iso (info.commodity);
1344 
1345     scu = scufunc (account);
1346 
1347     if (is_decimal_fraction (scu, &info.max_decimal_places))
1348     {
1349         if (is_iso)
1350             info.min_decimal_places = info.max_decimal_places;
1351         else
1352             info.min_decimal_places = 0;
1353     }
1354     else
1355         info.max_decimal_places = info.min_decimal_places = 0;
1356 
1357     info.use_separators = 1;
1358     info.use_symbol = use_symbol ? 1 : 0;
1359     info.use_locale = is_iso ? 1 : 0;
1360     info.monetary = 1;
1361     info.force_fit = 0;
1362     info.round = 0;
1363 
1364     return info;
1365 }
1366 
1367 GNCPrintAmountInfo
gnc_account_print_info(const Account * account,gboolean use_symbol)1368 gnc_account_print_info (const Account *account, gboolean use_symbol)
1369 {
1370     return gnc_account_print_info_helper(account, use_symbol,
1371                                          xaccAccountGetCommodity,
1372                                          xaccAccountGetCommoditySCU);
1373 }
1374 
1375 GNCPrintAmountInfo
gnc_split_amount_print_info(Split * split,gboolean use_symbol)1376 gnc_split_amount_print_info (Split *split, gboolean use_symbol)
1377 {
1378     if (!split)
1379     {
1380         GNCPrintAmountInfo info = gnc_default_share_print_info ();
1381         info.use_symbol = use_symbol;
1382         return info;
1383     }
1384 
1385     return gnc_account_print_info (xaccSplitGetAccount (split), use_symbol);
1386 }
1387 
1388 static GNCPrintAmountInfo
gnc_default_print_info_helper(int decplaces)1389 gnc_default_print_info_helper (int decplaces)
1390 {
1391     GNCPrintAmountInfo info;
1392 
1393     info.commodity = NULL;
1394 
1395     info.max_decimal_places = decplaces;
1396     info.min_decimal_places = 0;
1397 
1398     info.use_separators = 1;
1399     info.use_symbol = 0;
1400     info.use_locale = 1;
1401     info.monetary = 1;
1402     info.force_fit = 0;
1403     info.round = 0;
1404 
1405     return info;
1406 }
1407 
1408 GNCPrintAmountInfo
gnc_default_share_print_info(void)1409 gnc_default_share_print_info (void)
1410 {
1411     static GNCPrintAmountInfo info;
1412     static gboolean got_it = FALSE;
1413 
1414     if (!got_it)
1415     {
1416         info = gnc_default_print_info_helper (5);
1417         info.monetary = 0;
1418         got_it = TRUE;
1419     }
1420 
1421     return info;
1422 }
1423 
1424 GNCPrintAmountInfo
gnc_share_print_info_places(int decplaces)1425 gnc_share_print_info_places (int decplaces)
1426 {
1427     GNCPrintAmountInfo info;
1428 
1429     info = gnc_default_share_print_info ();
1430     info.max_decimal_places = decplaces;
1431     info.min_decimal_places = decplaces;
1432     info.force_fit = 1;
1433     info.round = 1;
1434     return info;
1435 }
1436 
1437 GNCPrintAmountInfo
gnc_price_print_info(const gnc_commodity * curr,gboolean use_symbol)1438 gnc_price_print_info (const gnc_commodity *curr, gboolean use_symbol)
1439 {
1440     GNCPrintAmountInfo info;
1441     gboolean force = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
1442                                          GNC_PREF_PRICES_FORCE_DECIMAL);
1443     info.commodity = curr;
1444 
1445     if (info.commodity)
1446     {
1447         int frac = gnc_commodity_get_fraction (curr);
1448         guint8 decplaces = 2;
1449         while (frac != 1 && (frac % 10) == 0 && (frac /= 10)) ++decplaces;
1450         info.max_decimal_places = decplaces;
1451         info.min_decimal_places = decplaces;
1452     }
1453     else
1454     {
1455         info.max_decimal_places = 6;
1456         info.min_decimal_places = 0;
1457     }
1458 
1459     info.use_separators = 1;
1460     info.use_symbol = use_symbol ? 1 : 0;
1461     info.use_locale = 1;
1462     info.monetary = 1;
1463 
1464     info.force_fit = force;
1465     info.round = force;
1466     return info;
1467 }
1468 
1469 GNCPrintAmountInfo
gnc_default_price_print_info(const gnc_commodity * curr)1470 gnc_default_price_print_info (const gnc_commodity *curr)
1471 {
1472     return gnc_price_print_info (curr, FALSE);
1473 }
1474 
1475 
1476 GNCPrintAmountInfo
gnc_integral_print_info(void)1477 gnc_integral_print_info (void)
1478 {
1479     static GNCPrintAmountInfo info;
1480     static gboolean got_it = FALSE;
1481 
1482     if (!got_it)
1483     {
1484         info = gnc_default_print_info_helper (0);
1485         got_it = TRUE;
1486     }
1487 
1488     return info;
1489 }
1490 
1491 /* Utility function for printing non-negative amounts */
1492 static int
PrintAmountInternal(char * buf,gnc_numeric val,const GNCPrintAmountInfo * info)1493 PrintAmountInternal(char *buf, gnc_numeric val, const GNCPrintAmountInfo *info)
1494 {
1495     struct lconv *lc = gnc_localeconv();
1496     int num_whole_digits;
1497     char temp_buf[128];
1498     gnc_numeric whole, rounding;
1499     int min_dp, max_dp;
1500     gboolean value_is_negative, value_is_decimal;
1501 
1502     g_return_val_if_fail (info != NULL, 0);
1503 
1504     if (gnc_numeric_check (val))
1505     {
1506         PWARN ("Bad numeric: %s.",
1507                gnc_numeric_errorCode_to_string(gnc_numeric_check (val)));
1508         *buf = '\0';
1509         return 0;
1510     }
1511 
1512     /* Print the absolute value, but remember sign */
1513     value_is_negative = gnc_numeric_negative_p (val);
1514     val = gnc_numeric_abs (val);
1515 
1516     /* Try to print as decimal. */
1517     value_is_decimal = gnc_numeric_to_decimal(&val, NULL);
1518     if (!value_is_decimal && info->force_fit && info->round)
1519     {
1520         /* if there's a commodity use 100x the commodity's fraction. N.B. This
1521          * assumes that commodity fractions are multiples of 10, a reasonable
1522          * assumption in 2018. Otherwise, if there's a reasonable
1523          * max_decimal_places, use that.
1524          */
1525         const gint64 denom = info->commodity ?
1526              gnc_commodity_get_fraction(info->commodity) * 100 :
1527              (info->max_decimal_places &&
1528                info->max_decimal_places <= maximum_decimals) ?
1529              pow_10[info->max_decimal_places] : pow_10[maximum_decimals];
1530         val = gnc_numeric_convert(val, denom, GNC_HOW_RND_ROUND_HALF_UP);
1531         value_is_decimal = gnc_numeric_to_decimal(&val, NULL);
1532     }
1533     min_dp = info->min_decimal_places;
1534     max_dp = info->max_decimal_places;
1535 
1536     /* Don to limit the number of decimal places _UNLESS_ force_fit is
1537      * true. */
1538     if (!info->force_fit)
1539         max_dp = 99;
1540 
1541     /* rounding? -- can only ROUND if force_fit is also true */
1542     if (value_is_decimal && info->round && info->force_fit)
1543     {
1544         rounding.num = 5; /* Limit the denom to 10^13 ~= 2^44, leaving max at ~524288 */
1545         rounding.denom = pow(10, max_dp + 1);
1546         val = gnc_numeric_add(val, rounding, val.denom,
1547                               GNC_HOW_DENOM_EXACT | GNC_HOW_RND_TRUNC);
1548 
1549         if (gnc_numeric_check(val))
1550         {
1551             PWARN ("Bad numeric from rounding: %s.",
1552                    gnc_numeric_errorCode_to_string(gnc_numeric_check (val)));
1553             *buf = '\0';
1554             return 0;
1555         }
1556     }
1557 
1558     /* calculate the integer part and the remainder */
1559     whole = gnc_numeric_convert(val, 1, GNC_HOW_RND_TRUNC);
1560     val = gnc_numeric_sub (val, whole, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
1561     if (gnc_numeric_check (val))
1562     {
1563         PWARN ("Problem with remainder: %s.",
1564                gnc_numeric_errorCode_to_string(gnc_numeric_check (val)));
1565         *buf = '\0';
1566         return 0;
1567     }
1568 
1569     // Value may now be decimal, for example if the factional part is zero
1570     value_is_decimal = gnc_numeric_to_decimal(&val, NULL);
1571     /* print the integer part without separators */
1572     sprintf(temp_buf, "%" G_GINT64_FORMAT, whole.num);
1573     num_whole_digits = strlen (temp_buf);
1574 
1575     if (!info->use_separators)
1576         strcpy (buf, temp_buf);
1577     else
1578     {
1579         int group_count;
1580         char *separator;
1581         char *temp_ptr;
1582         char *buf_ptr;
1583         char *group;
1584         gchar *rev_buf;
1585 
1586         if (info->monetary)
1587         {
1588             separator = lc->mon_thousands_sep;
1589             group = lc->mon_grouping;
1590         }
1591         else
1592         {
1593             separator = lc->thousands_sep;
1594             group = lc->grouping;
1595         }
1596 
1597         buf_ptr = buf;
1598         temp_ptr = &temp_buf[num_whole_digits - 1];
1599         group_count = 0;
1600 
1601         while (temp_ptr != temp_buf)
1602         {
1603             *buf_ptr++ = *temp_ptr--;
1604 
1605             if (*group != CHAR_MAX)
1606             {
1607                 group_count++;
1608 
1609                 if (group_count == *group)
1610                 {
1611                     g_utf8_strncpy(buf_ptr, separator, 1);
1612                     buf_ptr = g_utf8_find_next_char(buf_ptr, NULL);
1613                     group_count = 0;
1614 
1615                     /* Peek ahead at the next group code */
1616                     switch (group[1])
1617                     {
1618                         /* A null char means repeat the last group indefinitely */
1619                     case '\0':
1620                         break;
1621                         /* CHAR_MAX means no more grouping allowed */
1622                     case CHAR_MAX:
1623                         /* fall through */
1624                         /* Anything else means another group size */
1625                     default:
1626                         group++;
1627                         break;
1628                     }
1629                 }
1630             }
1631         }
1632 
1633         /* We built the string backwards, now reverse */
1634         *buf_ptr++ = *temp_ptr;
1635         *buf_ptr = '\0';
1636         rev_buf = g_utf8_strreverse(buf, -1);
1637         strcpy (buf, rev_buf);
1638         g_free(rev_buf);
1639     } /* endif */
1640 
1641     /* at this point, buf contains the whole part of the number */
1642 
1643     /* If it's not decimal, print the fraction as an expression. */
1644     if (!value_is_decimal)
1645     {
1646         val = gnc_numeric_reduce (val);
1647 
1648         if (val.denom > 0)
1649             sprintf (temp_buf, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
1650                      val.num, val.denom);
1651         else
1652             sprintf (temp_buf, "%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT,
1653                      val.num, -val.denom);
1654 
1655         if (whole.num == 0)
1656             *buf = '\0';
1657         else if (value_is_negative)
1658             strcat(buf, " - ");
1659         else
1660             strcat(buf, " + ");
1661 
1662         strcat (buf, temp_buf);
1663     }
1664     else
1665     {
1666         char *decimal_point;
1667         guint8 num_decimal_places = 0;
1668         char *temp_ptr = temp_buf;
1669 
1670         decimal_point = info->monetary
1671                         ? lc->mon_decimal_point
1672                         : lc->decimal_point;
1673         g_utf8_strncpy(temp_ptr, decimal_point, 1);
1674         temp_ptr = g_utf8_find_next_char(temp_ptr, NULL);
1675 
1676         while (!gnc_numeric_zero_p (val)
1677                 && (val.denom != 1)
1678                 && (num_decimal_places < max_dp))
1679         {
1680             gint64 digit;
1681 
1682             val.denom = val.denom / 10;
1683 
1684             digit = val.num / val.denom;
1685 
1686             *temp_ptr++ = digit + '0';
1687             num_decimal_places++;
1688 
1689             val.num = val.num - (digit * val.denom);
1690         }
1691 
1692         while (num_decimal_places < min_dp)
1693         {
1694             *temp_ptr++ = '0';
1695             num_decimal_places++;
1696         }
1697 
1698         /* cap the end and move to the last character */
1699         *temp_ptr-- = '\0';
1700 
1701         /* Here we strip off trailing decimal zeros per the argument. */
1702         while (*temp_ptr == '0' && num_decimal_places > min_dp)
1703         {
1704             *temp_ptr-- = '\0';
1705             num_decimal_places--;
1706         }
1707 
1708         if (num_decimal_places > max_dp)
1709         {
1710             PWARN ("max_decimal_places too small; limit %d, value %s%s",
1711                    info->max_decimal_places, buf, temp_buf);
1712         }
1713 
1714         if (num_decimal_places > 0)
1715             strcat (buf, temp_buf);
1716     }
1717 
1718     return strlen(buf);
1719 }
1720 
1721 /**
1722  * @param bufp Should be at least 64 chars.
1723  **/
1724 int
xaccSPrintAmount(char * bufp,gnc_numeric val,GNCPrintAmountInfo info)1725 xaccSPrintAmount (char * bufp, gnc_numeric val, GNCPrintAmountInfo info)
1726 {
1727     struct lconv *lc;
1728 
1729     char *orig_bufp = bufp;
1730     const char *currency_symbol;
1731     const char *sign;
1732 
1733     char cs_precedes;
1734     char sep_by_space;
1735     char sign_posn;
1736 
1737     gboolean print_sign = TRUE;
1738     gboolean print_absolute = FALSE;
1739 
1740     if (!bufp)
1741         return 0;
1742 
1743     lc = gnc_localeconv();
1744     if (info.use_locale)
1745         if (gnc_numeric_negative_p (val))
1746         {
1747             cs_precedes  = lc->n_cs_precedes;
1748             sep_by_space = lc->n_sep_by_space;
1749         }
1750         else
1751         {
1752             cs_precedes  = lc->p_cs_precedes;
1753             sep_by_space = lc->p_sep_by_space;
1754         }
1755     else
1756     {
1757         cs_precedes = TRUE;
1758         sep_by_space = TRUE;
1759     }
1760 
1761     if (info.commodity && info.use_symbol)
1762     {
1763         currency_symbol = gnc_commodity_get_nice_symbol (info.commodity);
1764         if (!gnc_commodity_is_iso (info.commodity))
1765         {
1766             cs_precedes  = FALSE;
1767             sep_by_space = TRUE;
1768         }
1769     }
1770     else /* !info.use_symbol || !info.commodity */
1771         currency_symbol = "";
1772 
1773     if (gnc_numeric_negative_p (val))
1774     {
1775         sign = lc->negative_sign;
1776         sign_posn = lc->n_sign_posn;
1777     }
1778     else
1779     {
1780         sign = lc->positive_sign;
1781         sign_posn = lc->p_sign_posn;
1782     }
1783 
1784     if (gnc_numeric_zero_p (val) || (sign == NULL) || (sign[0] == 0))
1785         print_sign = FALSE;
1786 
1787     /* See if we print sign now */
1788     if (print_sign && (sign_posn == 1))
1789         bufp = g_stpcpy(bufp, sign);
1790 
1791     /* Now see if we print currency */
1792     if (cs_precedes)
1793     {
1794         /* See if we print sign now */
1795         if (print_sign && (sign_posn == 3))
1796             bufp = g_stpcpy(bufp, sign);
1797 
1798         if (info.use_symbol)
1799         {
1800             bufp = g_stpcpy(bufp, currency_symbol);
1801             if (sep_by_space)
1802                 bufp = g_stpcpy(bufp, " ");
1803         }
1804 
1805         /* See if we print sign now */
1806         if (print_sign && (sign_posn == 4))
1807             bufp = g_stpcpy(bufp, sign);
1808     }
1809 
1810     /* Now see if we print parentheses */
1811     if (print_sign && (sign_posn == 0))
1812     {
1813         bufp = g_stpcpy(bufp, "(");
1814         print_absolute = TRUE;
1815     }
1816 
1817     /* Now print the value */
1818     bufp += PrintAmountInternal(bufp,
1819                                 print_absolute ? gnc_numeric_abs(val) : val,
1820                                 &info);
1821 
1822     /* Now see if we print parentheses */
1823     if (print_sign && (sign_posn == 0))
1824         bufp = g_stpcpy(bufp, ")");
1825 
1826     /* Now see if we print currency */
1827     if (!cs_precedes)
1828     {
1829         /* See if we print sign now */
1830         if (print_sign && (sign_posn == 3))
1831             bufp = g_stpcpy(bufp, sign);
1832 
1833         if (info.use_symbol)
1834         {
1835             if (sep_by_space)
1836                 bufp = g_stpcpy(bufp, " ");
1837             bufp = g_stpcpy(bufp, currency_symbol);
1838         }
1839 
1840         /* See if we print sign now */
1841         if (print_sign && (sign_posn == 4))
1842             bufp = g_stpcpy(bufp, sign);
1843     }
1844 
1845     /* See if we print sign now */
1846     if (print_sign && (sign_posn == 2))
1847         bufp = g_stpcpy(bufp, sign);
1848 
1849     /* return length of printed string */
1850     return (bufp - orig_bufp);
1851 }
1852 
1853 const char *
xaccPrintAmount(gnc_numeric val,GNCPrintAmountInfo info)1854 xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info)
1855 {
1856     /* hack alert -- this is not thread safe ... */
1857     static char buf[1024];
1858 
1859     if (!xaccSPrintAmount (buf, val, info))
1860         buf[0] = '\0';
1861 
1862     /* its OK to return buf, since we declared it static */
1863     return buf;
1864 }
1865 
1866 
1867 /********************************************************************\
1868  ********************************************************************/
1869 
1870 #define FUDGE .00001
1871 
1872 /* This function is basically untranslatable. I'd
1873    guess out of the 29 translations we have, 20 will have their number
1874    wordings in a totally different way than English has (not to
1875    mention gender-dependent number endings). Which means this
1876    word-by-word translation will be useless or even plain
1877    wrong. For this reason, we don't even start to pretend a
1878    word-by-word translation would be of any use, so we don't mark any
1879    of these strings for translation. cstim, 2007-04-15. */
1880 static gchar *small_numbers[] =
1881 {
1882     /* Translators: This section is for generating the "amount, in
1883        words" field when printing a check. This function gets the
1884        wording right for English, but unfortunately not for most other
1885        languages. Decide for yourself whether the check printing is
1886        actually needed in your language; if not, you can safely skip the
1887        translation of all of these strings.  */
1888     "Zero", "One", "Two", "Three", "Four",
1889     "Five", "Six", "Seven", "Eight", "Nine",
1890     "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
1891     "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen",
1892     "Twenty"
1893 };
1894 static gchar *medium_numbers[] =
1895 {
1896     "Zero", "Ten", "Twenty", "Thirty", "Forty",
1897     "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"
1898 };
1899 static gchar *big_numbers[] =
1900 {
1901     /* Translators: This is the word for the number 10^2 */
1902     "Hundred",
1903     /* Translators: This is the word for the number 10^3 */
1904     "Thousand",
1905     /* Translators: This is the word for the number 10^6, one thousand
1906        thousands. */
1907     "Million",
1908     /* Translators: This is the word for the number 10^9, one thousand
1909        millions. WATCH OUT: In British English and many other languages
1910        this word is used for 10^12 which is one million millions! In
1911        contrast to this, here in GnuCash this is used in the American
1912        English meaning of 10^9.  */
1913     "Billion",
1914     /* Translators: This is the word for the number 10^12, one million
1915        millions. */
1916     "Trillion",
1917     /* Translators: This is the word for the number 10^15 */
1918     "Quadrillion",
1919     /* Translators: This is the word for the number 10^18 */
1920     "Quintillion"
1921 };
1922 
1923 static gchar *
integer_to_words(gint64 val)1924 integer_to_words(gint64 val)
1925 {
1926     gint64 log_val, pow_val, this_part;
1927     GString *result;
1928     gchar *tmp;
1929 
1930     if (val == 0)
1931         return g_strdup("zero");
1932     if (val < 0)
1933         val = -val;
1934 
1935     result = g_string_sized_new(100);
1936 
1937     while (val >= 1000)
1938     {
1939         log_val = log10(val) / 3 + FUDGE;
1940         pow_val = exp(log_val * 3 * G_LN10) + FUDGE;
1941         this_part = val / pow_val;
1942         val -= this_part * pow_val;
1943         tmp = integer_to_words(this_part);
1944         g_string_append_printf(result, "%s %s ", tmp,
1945                                gettext(big_numbers[log_val]));
1946         g_free(tmp);
1947     }
1948 
1949     if (val >= 100)
1950     {
1951         this_part = val / 100;
1952         val -= this_part * 100;
1953         g_string_append_printf(result, "%s %s ",
1954                                gettext(small_numbers[this_part]),
1955                                gettext(big_numbers[0]));
1956     }
1957 
1958     if (val > 20)
1959     {
1960         this_part = val / 10;
1961         val -= this_part * 10;
1962         g_string_append(result, gettext(medium_numbers[this_part]));
1963         g_string_append_c(result, ' ');
1964     }
1965 
1966     if (val > 0)
1967     {
1968         this_part = val;
1969         g_string_append(result, gettext(small_numbers[this_part]));
1970         g_string_append_c(result, ' ');
1971     }
1972 
1973     result = g_string_truncate(result, result->len - 1);
1974     return g_string_free(result, FALSE);
1975 }
1976 
1977 #ifdef _MSC_VER
round(double x)1978 static double round(double x)
1979 {
1980     // A simple round() implementation because MSVC doesn't seem to have that
1981     return floor(x + 0.5);
1982 }
1983 #endif
1984 
1985 gchar *
number_to_words(gdouble val,gint64 denom)1986 number_to_words(gdouble val, gint64 denom)
1987 {
1988     gint64 int_part, frac_part;
1989     gchar *int_string, *nomin_string, *denom_string, *full_string;
1990 
1991     if (val < 0) val = -val;
1992     if (denom < 0) denom = -denom;
1993 
1994     int_part = floor(val);
1995     frac_part = round((val - int_part) * denom);
1996 
1997     int_string = integer_to_words(int_part);
1998     /* Inside of the gettext macro _(...) we must not use any macros but
1999        only plain string literals. For this reason, convert the strings
2000        separately. */
2001     nomin_string = g_strdup_printf("%02" G_GINT64_FORMAT, frac_part);
2002     denom_string = g_strdup_printf("%" G_GINT64_FORMAT, denom);
2003     full_string =
2004         /* Translators: This is for the "amount, in words" field in check
2005            printing. The first %s is the integer amount of dollars (or
2006            whatever currency), the second and third %s the cent amount as
2007            a fraction, e.g. 47/100.  */
2008         g_strdup_printf("%s and %s/%s",
2009                         int_string, nomin_string, denom_string);
2010     g_free(int_string);
2011     g_free(nomin_string);
2012     g_free(denom_string);
2013     return full_string;
2014 }
2015 
2016 gchar *
numeric_to_words(gnc_numeric val)2017 numeric_to_words(gnc_numeric val)
2018 {
2019     return number_to_words(gnc_numeric_to_double(val),
2020                            gnc_numeric_denom(val));
2021 }
2022 
2023 const gchar *
printable_value(gdouble val,gint denom)2024 printable_value (gdouble val, gint denom)
2025 {
2026     GNCPrintAmountInfo info;
2027     gnc_numeric num;
2028 
2029     num = gnc_numeric_create(round(val * denom), denom);
2030     info = gnc_share_print_info_places(log10(denom));
2031     return xaccPrintAmount (num, info);
2032 }
2033 
2034 /********************************************************************\
2035  * xaccParseAmount                                                  *
2036  *   parses amount strings using locale data                        *
2037  *                                                                  *
2038  * Args: in_str   -- pointer to string rep of num                   *
2039  *       monetary -- boolean indicating whether value is monetary   *
2040  *       result   -- pointer to result location, may be NULL        *
2041  *       endstr   -- used to store first digit not used in parsing  *
2042  * Return: gboolean -- TRUE if a number found and parsed            *
2043  *                     If FALSE, result is not changed              *
2044 \********************************************************************/
2045 
2046 /* Parsing state machine states */
2047 typedef enum
2048 {
2049     START_ST,       /* Parsing initial whitespace */
2050     NEG_ST,         /* Parsed a negative sign or a left paren */
2051     NUMER_ST,   /* Parsing digits before grouping and decimal characters */
2052     FRAC_ST,        /* Parsing the fractional portion of a number */
2053     DONE_ST,        /* Finished, number is correct module grouping constraints */
2054     NO_NUM_ST       /* Finished, number was malformed */
2055 } ParseState;
2056 
2057 #define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST))
2058 
2059 static inline long long int
multiplier(int num_decimals)2060 multiplier (int num_decimals)
2061 {
2062     switch (num_decimals)
2063     {
2064     case 12:
2065         return 1000000000000;
2066     case 11:
2067         return 100000000000;
2068     case 10:
2069         return 10000000000;
2070     case 9:
2071         return 1000000000;
2072     case 8:
2073         return 100000000;
2074     case 7:
2075         return 10000000;
2076     case 6:
2077         return 1000000;
2078     case 5:
2079         return 100000;
2080     case 4:
2081         return 10000;
2082     case 3:
2083         return 1000;
2084     case 2:
2085         return 100;
2086     case 1:
2087         return 10;
2088     case 0:
2089          return 1;
2090     default:
2091         PERR("bad fraction length");
2092         g_assert_not_reached();
2093         break;
2094     }
2095 
2096     return 1;
2097 }
2098 
2099 gboolean
xaccParseAmount(const char * in_str,gboolean monetary,gnc_numeric * result,char ** endstr)2100 xaccParseAmount (const char * in_str, gboolean monetary, gnc_numeric *result,
2101                  char **endstr)
2102 {
2103     return xaccParseAmountPosSign (in_str, monetary, result, endstr, FALSE);
2104 }
2105 
2106 gboolean
xaccParseAmountPosSign(const char * in_str,gboolean monetary,gnc_numeric * result,char ** endstr,gboolean skip)2107 xaccParseAmountPosSign (const char * in_str, gboolean monetary, gnc_numeric *result,
2108                         char **endstr, gboolean skip)
2109 {
2110     struct lconv *lc = gnc_localeconv();
2111 
2112     gunichar negative_sign;
2113     gunichar decimal_point;
2114     gunichar group_separator;
2115     gchar *ignore = NULL;
2116     char *plus_sign = "+";
2117 
2118     negative_sign = g_utf8_get_char(lc->negative_sign);
2119     if (monetary)
2120     {
2121         group_separator = g_utf8_get_char(lc->mon_thousands_sep);
2122         decimal_point = g_utf8_get_char(lc->mon_decimal_point);
2123     }
2124     else
2125     {
2126         group_separator = g_utf8_get_char(lc->thousands_sep);
2127         decimal_point = g_utf8_get_char(lc->decimal_point);
2128     }
2129 
2130     if (skip)
2131     {
2132         /* We want the locale's positive sign to be ignored.
2133         * If the locale doesn't specify one, we assume "+" as
2134         * an optional positive sign and ignore that */
2135         ignore = lc->positive_sign;
2136         if (!ignore || !*ignore)
2137             ignore = plus_sign;
2138     }
2139 
2140     return xaccParseAmountExtended(in_str, monetary, negative_sign,
2141                                    decimal_point, group_separator,
2142                                    ignore, result, endstr);
2143 }
2144 
2145 gboolean
xaccParseAmountExtended(const char * in_str,gboolean monetary,gunichar negative_sign,gunichar decimal_point,gunichar group_separator,const char * ignore_list,gnc_numeric * result,char ** endstr)2146 xaccParseAmountExtended (const char * in_str, gboolean monetary,
2147                          gunichar negative_sign, gunichar decimal_point,
2148                          gunichar group_separator, const char *ignore_list,
2149                          gnc_numeric *result, char **endstr)
2150 {
2151     gboolean is_negative;
2152     gboolean got_decimal;
2153     gboolean need_paren;
2154     long long int numer;
2155     long long int denom;
2156     int count;
2157 
2158     ParseState state;
2159 
2160     const gchar *in;
2161     gunichar uc;
2162     gchar *out_str;
2163     gchar *out;
2164 
2165     /* Initialize *endstr to in_str */
2166     if (endstr != NULL)
2167         *endstr = (char *) in_str;
2168 
2169     if (in_str == NULL)
2170         return FALSE;
2171 
2172     if (!g_utf8_validate(in_str, -1, &in))
2173     {
2174         printf("Invalid utf8 string '%s'. Bad character at position %ld.\n",
2175                in_str, g_utf8_pointer_to_offset (in_str, in));
2176         return FALSE;
2177     }
2178 
2179     /* 'out_str' will be used to store digits for numeric conversion.
2180      * 'out' will be used to traverse out_str. */
2181     out = out_str = g_new(gchar, strlen(in_str) + 128);
2182 
2183     /* 'in' is used to traverse 'in_str'. */
2184     in = in_str;
2185 
2186     is_negative = FALSE;
2187     got_decimal = FALSE;
2188     need_paren = FALSE;
2189     numer = 0;
2190     denom = 1;
2191 
2192     /* Initialize the state machine */
2193     state = START_ST;
2194 
2195     /* This while loop implements a state machine for parsing numbers. */
2196     while (TRUE)
2197     {
2198         ParseState next_state = state;
2199 
2200         uc = g_utf8_get_char(in);
2201 
2202         /* Ignore anything in the 'ignore list' */
2203         if (ignore_list && uc && g_utf8_strchr(ignore_list, -1, uc) != NULL)
2204         {
2205             in = g_utf8_next_char(in);
2206             continue;
2207         }
2208 
2209         /* Note we never need to check for the end of 'in_str' explicitly.
2210          * The 'else' clauses on all the state transitions will handle that. */
2211         switch (state)
2212         {
2213             /* START_ST means we have parsed 0 or more whitespace characters */
2214         case START_ST:
2215             if (g_unichar_isdigit(uc))
2216             {
2217                 count = g_unichar_to_utf8(uc, out);
2218                 out += count; /* we record the digits themselves in out_str
2219                          * for later conversion by libc routines */
2220                 next_state = NUMER_ST;
2221             }
2222             else if (uc == decimal_point)
2223             {
2224                 next_state = FRAC_ST;
2225             }
2226             else if (g_unichar_isspace(uc))
2227             {
2228             }
2229             else if (uc == negative_sign)
2230             {
2231                 is_negative = TRUE;
2232                 next_state = NEG_ST;
2233             }
2234             else if (uc == '(')
2235             {
2236                 is_negative = TRUE;
2237                 need_paren = TRUE;
2238                 next_state = NEG_ST;
2239             }
2240             else
2241             {
2242                 next_state = NO_NUM_ST;
2243             }
2244 
2245             break;
2246 
2247             /* NEG_ST means we have just parsed a negative sign. For now,
2248              * we only recognize formats where the negative sign comes first. */
2249         case NEG_ST:
2250             if (g_unichar_isdigit(uc))
2251             {
2252                 count = g_unichar_to_utf8(uc, out);
2253                 out += count;
2254                 next_state = NUMER_ST;
2255             }
2256             else if (uc == decimal_point)
2257             {
2258                 next_state = FRAC_ST;
2259             }
2260             else if (g_unichar_isspace(uc))
2261             {
2262             }
2263             else
2264             {
2265                 next_state = NO_NUM_ST;
2266             }
2267 
2268             break;
2269 
2270             /* NUMER_ST means we have started parsing the number, but
2271              * have not encountered a decimal separator. */
2272         case NUMER_ST:
2273             if (g_unichar_isdigit(uc))
2274             {
2275                 count = g_unichar_to_utf8(uc, out);
2276                 out += count;
2277             }
2278             else if (uc == decimal_point)
2279             {
2280                 next_state = FRAC_ST;
2281             }
2282             else if (uc == group_separator)
2283             {
2284                  ; //ignore it
2285             }
2286             else if (uc == ')' && need_paren)
2287             {
2288                 next_state = DONE_ST;
2289                 need_paren = FALSE;
2290             }
2291             else
2292             {
2293                 next_state = DONE_ST;
2294             }
2295 
2296             break;
2297 
2298             /* FRAC_ST means we are now parsing fractional digits. */
2299         case FRAC_ST:
2300             if (g_unichar_isdigit(uc))
2301             {
2302                 count = g_unichar_to_utf8(uc, out);
2303                 out += count;
2304             }
2305             else if (uc == decimal_point)
2306             {
2307                 /* If a subsequent decimal point is also whitespace,
2308                  * assume it was intended as such and stop parsing.
2309                  * Otherwise, there is a problem. */
2310                 if (g_unichar_isspace(decimal_point))
2311                     next_state = DONE_ST;
2312                 else
2313                     next_state = NO_NUM_ST;
2314             }
2315             else if (uc == group_separator)
2316             {
2317                 /* If a subsequent group separator is also whitespace,
2318                  * assume it was intended as such and stop parsing.
2319                  * Otherwise ignore it. */
2320                 if (g_unichar_isspace(group_separator))
2321                     next_state = DONE_ST;
2322             }
2323             else if (uc == ')' && need_paren)
2324             {
2325                 next_state = DONE_ST;
2326                 need_paren = FALSE;
2327             }
2328             else
2329             {
2330                 next_state = DONE_ST;
2331             }
2332 
2333             break;
2334 
2335         default:
2336             PERR("bad state");
2337             g_assert_not_reached();
2338             break;
2339         }
2340 
2341         /* If we're moving into the FRAC_ST or out of the machine
2342          * without going through FRAC_ST, record the integral value. */
2343         if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
2344                 ((next_state == DONE_ST) && !got_decimal))
2345         {
2346             *out = '\0';
2347 
2348             if (*out_str != '\0' && sscanf(out_str, QOF_SCANF_LLD, &numer) < 1)
2349             {
2350                 next_state = NO_NUM_ST;
2351             }
2352             else if (next_state == FRAC_ST)
2353             {
2354                 /* reset the out pointer to record the fraction */
2355                 out = out_str;
2356                 *out = '\0';
2357 
2358                 got_decimal = TRUE;
2359             }
2360         }
2361 
2362         state = next_state;
2363         if (done_state (state))
2364             break;
2365 
2366         in = g_utf8_next_char(in);
2367     }
2368 
2369     /* If there was an error, just quit */
2370     if (need_paren || (state == NO_NUM_ST))
2371     {
2372         g_free(out_str);
2373         return FALSE;
2374     }
2375 
2376     /* Cap the end of the fraction string, if any */
2377     *out = '\0';
2378 
2379     /* Add in fractional value */
2380     if (got_decimal && (*out_str != '\0'))
2381     {
2382         size_t len;
2383         long long int fraction;
2384 
2385         len = strlen(out_str);
2386 
2387         if (len > 12)
2388         {
2389             out_str[12] = '\0';
2390             len = 12;
2391         }
2392 
2393         if (sscanf (out_str, QOF_SCANF_LLD, &fraction) < 1)
2394         {
2395             g_free(out_str);
2396             return FALSE;
2397         }
2398 
2399         denom = multiplier(len);
2400         numer *= denom;
2401         numer += fraction;
2402     }
2403     else if (monetary && auto_decimal_enabled && !got_decimal)
2404     {
2405         if ((auto_decimal_places > 0) && (auto_decimal_places <= 12))
2406         {
2407             denom = multiplier(auto_decimal_places);
2408 
2409             /* No need to multiply numer by denom at this point,
2410              * since by specifying the auto decimal places the
2411              * user has effectively determined the scaling factor
2412              * for the numerator they entered.
2413              */
2414         }
2415     }
2416 
2417     if (result != NULL)
2418     {
2419         *result = gnc_numeric_create (numer, denom);
2420         if (is_negative)
2421             *result = gnc_numeric_neg (*result);
2422     }
2423 
2424     if (endstr != NULL)
2425         *endstr = (char *) in;
2426 
2427     g_free (out_str);
2428 
2429     return TRUE;
2430 }
2431 
2432 /* enable/disable the auto_decimal_enabled option */
2433 static void
gnc_set_auto_decimal_enabled(gpointer settings,gchar * key,gpointer user_data)2434 gnc_set_auto_decimal_enabled (gpointer settings, gchar *key, gpointer user_data)
2435 {
2436     auto_decimal_enabled =
2437             gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
2438 }
2439 
2440 /* set the number of auto decimal places to use */
2441 static void
gnc_set_auto_decimal_places(gpointer settings,gchar * key,gpointer user_data)2442 gnc_set_auto_decimal_places (gpointer settings, gchar *key, gpointer user_data)
2443 {
2444     auto_decimal_places =
2445             gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
2446 }
2447 
2448 static void
gnc_auto_decimal_init(void)2449 gnc_auto_decimal_init (void)
2450 {
2451     auto_decimal_enabled =
2452         gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
2453     auto_decimal_places =
2454         gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
2455 }
2456 
2457 void
gnc_ui_util_init(void)2458 gnc_ui_util_init (void)
2459 {
2460     gnc_configure_account_separator ();
2461     gnc_auto_decimal_init();
2462 
2463     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR,
2464                           gnc_configure_account_separator, NULL);
2465     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE,
2466                           gnc_configure_reverse_balance, NULL);
2467     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT,
2468                           gnc_configure_reverse_balance, NULL);
2469     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP,
2470                           gnc_configure_reverse_balance, NULL);
2471     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_LOCALE,
2472                           gnc_currency_changed_cb, NULL);
2473     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_OTHER,
2474                           gnc_currency_changed_cb, NULL);
2475     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_OTHER,
2476                           gnc_currency_changed_cb, NULL);
2477     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_LOCALE,
2478                           gnc_currency_changed_cb, NULL);
2479     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_OTHER,
2480                           gnc_currency_changed_cb, NULL);
2481     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_OTHER,
2482                           gnc_currency_changed_cb, NULL);
2483     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT,
2484                           gnc_set_auto_decimal_enabled, NULL);
2485     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES,
2486                           gnc_set_auto_decimal_places, NULL);
2487 
2488 }
2489 
2490 void
gnc_ui_util_remove_registered_prefs(void)2491 gnc_ui_util_remove_registered_prefs (void)
2492 {
2493     // remove the registered pref call backs above
2494     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2495                                  GNC_PREF_ACCOUNT_SEPARATOR,
2496                                  gnc_configure_account_separator, NULL);
2497     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2498                                  GNC_PREF_REVERSED_ACCTS_NONE,
2499                                  gnc_configure_reverse_balance, NULL);
2500     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2501                                  GNC_PREF_REVERSED_ACCTS_CREDIT,
2502                                  gnc_configure_reverse_balance, NULL);
2503     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2504                                  GNC_PREF_REVERSED_ACCTS_INC_EXP,
2505                                  gnc_configure_reverse_balance, NULL);
2506     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2507                                  GNC_PREF_CURRENCY_CHOICE_LOCALE,
2508                                  gnc_currency_changed_cb, NULL);
2509     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2510                                  GNC_PREF_CURRENCY_CHOICE_OTHER,
2511                                  gnc_currency_changed_cb, NULL);
2512     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2513                                  GNC_PREF_CURRENCY_OTHER,
2514                                  gnc_currency_changed_cb, NULL);
2515     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2516                                  GNC_PREF_CURRENCY_CHOICE_LOCALE,
2517                                  gnc_currency_changed_cb, NULL);
2518     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2519                                  GNC_PREF_CURRENCY_CHOICE_OTHER,
2520                                  gnc_currency_changed_cb, NULL);
2521     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2522                                  GNC_PREF_CURRENCY_OTHER,
2523                                  gnc_currency_changed_cb, NULL);
2524     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2525                                  GNC_PREF_AUTO_DECIMAL_POINT,
2526                                  gnc_set_auto_decimal_enabled, NULL);
2527     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2528                                  GNC_PREF_AUTO_DECIMAL_PLACES,
2529                                  gnc_set_auto_decimal_places, NULL);
2530 }
2531 
2532 static gboolean
unichar_is_cntrl(gunichar uc)2533 unichar_is_cntrl (gunichar uc)
2534 {
2535     if (uc < 0x20 || (uc > 0x7e && uc < 0xa0))
2536         return TRUE;
2537     else
2538         return FALSE;
2539 }
2540 
2541 gchar *
gnc_filter_text_for_control_chars(const gchar * text)2542 gnc_filter_text_for_control_chars (const gchar *text)
2543 {
2544     gchar *normal_text, *nt;
2545     GString *filtered;
2546     gboolean cntrl = FALSE;
2547     gboolean text_found = FALSE;
2548 
2549     if (!text)
2550         return NULL;
2551 
2552     if (!g_utf8_validate (text, -1, NULL))
2553         return NULL;
2554 
2555     normal_text = g_utf8_normalize (text, -1, G_NORMALIZE_ALL_COMPOSE);
2556 
2557     filtered = g_string_sized_new (strlen (normal_text) + 1);
2558 
2559     nt = normal_text;
2560 
2561     while (*nt)
2562     {
2563         gunichar uc = g_utf8_get_char (nt);
2564 
2565         // check for starting with control characters
2566         if (unichar_is_cntrl (uc) && !text_found)
2567         {
2568             nt = g_utf8_next_char (nt);
2569             continue;
2570         }
2571         // check for alpha, num and punctuation
2572         if (!unichar_is_cntrl (uc))
2573         {
2574             filtered = g_string_append_unichar (filtered, uc);
2575             text_found = TRUE;
2576         }
2577         // check for control characters after text
2578         if (unichar_is_cntrl (uc))
2579             cntrl = TRUE;
2580 
2581         nt = g_utf8_next_char (nt);
2582 
2583         if (cntrl) // if control characters in text replace with space
2584         {
2585             gunichar uc2 = g_utf8_get_char (nt);
2586 
2587             if (!unichar_is_cntrl (uc2))
2588                 filtered = g_string_append_unichar (filtered, ' ');
2589         }
2590         cntrl = FALSE;
2591     }
2592     g_free (normal_text);
2593     return g_string_free (filtered, FALSE);
2594 }
2595 
2596 void
gnc_filter_text_set_cursor_position(const gchar * incoming_text,const gchar * symbol,gint * cursor_position)2597 gnc_filter_text_set_cursor_position (const gchar *incoming_text,
2598                                      const gchar *symbol,
2599                                      gint *cursor_position)
2600 {
2601     gint text_len;
2602     gint num = 0;
2603 
2604     if (*cursor_position == 0)
2605         return;
2606 
2607     if (!incoming_text || !symbol)
2608         return;
2609 
2610     if (g_strrstr (incoming_text, symbol) == NULL)
2611         return;
2612 
2613     text_len = g_utf8_strlen (incoming_text, -1);
2614 
2615     for (gint x = 0; x < text_len; x++)
2616     {
2617         gchar *temp = g_utf8_offset_to_pointer (incoming_text, x);
2618 
2619         if (g_str_has_prefix (temp, symbol))
2620             num++;
2621 
2622         if (g_strrstr (temp, symbol) == NULL)
2623             break;
2624     }
2625     *cursor_position = *cursor_position - (num * g_utf8_strlen (symbol, -1));
2626 }
2627 
2628 gchar *
gnc_filter_text_for_currency_symbol(const gchar * incoming_text,const gchar * symbol)2629 gnc_filter_text_for_currency_symbol (const gchar *incoming_text,
2630                                      const gchar *symbol)
2631 {
2632     gchar *ret_text = NULL;
2633     gchar **split;
2634 
2635     if (!incoming_text)
2636         return NULL;
2637 
2638     if (!symbol)
2639        return g_strdup (incoming_text);
2640 
2641     if (g_strrstr (incoming_text, symbol) == NULL)
2642         return g_strdup (incoming_text);
2643 
2644     split = g_strsplit (incoming_text, symbol, -1);
2645 
2646     ret_text = g_strjoinv (NULL, split);
2647 
2648     g_strfreev (split);
2649     return ret_text;
2650 }
2651 
2652 gchar *
gnc_filter_text_for_currency_commodity(const gnc_commodity * comm,const gchar * incoming_text,const gchar ** symbol)2653 gnc_filter_text_for_currency_commodity (const gnc_commodity *comm,
2654                                         const gchar *incoming_text,
2655                                         const gchar **symbol)
2656 {
2657     if (!incoming_text)
2658     {
2659         *symbol = NULL;
2660         return NULL;
2661     }
2662 
2663     if (!gnc_commodity_is_currency (comm))
2664     {
2665         *symbol = NULL;
2666         return g_strdup (incoming_text);
2667     }
2668 
2669     if (comm)
2670         *symbol = gnc_commodity_get_nice_symbol (comm);
2671     else
2672         *symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
2673 
2674     return gnc_filter_text_for_currency_symbol (incoming_text, *symbol);
2675 }
2676