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