GnuCash  5.6-150-g038405b370+
gnc-ui-util.cpp
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 <ctype.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <locale.h>
36 #include <math.h>
37 #if defined(G_OS_WIN32) && !defined(_MSC_VER)
38 # include <pow.h>
39 #endif
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <cinttypes>
45 #include <unicode/listformatter.h>
46 
47 #include "qof.h"
48 #include "gnc-prefs.h"
49 #include "Account.h"
50 #include "Transaction.h"
51 #include "gnc-engine.h"
52 #include "gnc-features.h"
53 #include "gnc-hooks.h"
54 #include "gnc-session.h"
55 #include "engine-helpers.h"
56 #include "gnc-locale-utils.h"
57 
58 #define GNC_PREF_CURRENCY_CHOICE_LOCALE "currency-choice-locale"
59 #define GNC_PREF_CURRENCY_CHOICE_OTHER "currency-choice-other"
60 #define GNC_PREF_CURRENCY_OTHER "currency-other"
61 #define GNC_PREF_REVERSED_ACCTS_NONE "reversed-accounts-none"
62 #define GNC_PREF_REVERSED_ACCTS_CREDIT "reversed-accounts-credit"
63 #define GNC_PREF_REVERSED_ACCTS_INC_EXP "reversed-accounts-incomeexpense"
64 #define GNC_PREF_PRICES_FORCE_DECIMAL "force-price-decimal"
65 
66 using UniStr = icu::UnicodeString;
67 
68 static QofLogModule log_module = GNC_MOD_GUI;
69 
70 static bool auto_decimal_enabled = false;
71 static int auto_decimal_places = 2; /* default, can be changed */
72 
73 static bool reverse_balance_inited = false;
74 static bool reverse_type[NUM_ACCOUNT_TYPES];
75 
76 /* Cache currency ISO codes and only look them up in the settings when
77  * absolutely necessary. Can't cache a pointer to the data structure
78  * as that will change any time the book changes. */
79 static char* user_default_currency = nullptr;
80 static char* user_report_currency = nullptr;
81 constexpr int maximum_decimals = 15;
82 constexpr int64_t pow_10[] = {1, 10, 100, 1000, 10000, 100000, 1000000,
83  10000000, 100000000, 1000000000, 10000000000,
84  100000000000, 1000000000000, 10000000000000,
85  100000000000000, 1000000000000000};
86 
87 char*
88 gnc_normalize_account_separator (const gchar* separator)
89 {
90  char* new_sep=nullptr;
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
114 gnc_configure_account_separator (void)
115 {
116  auto string = gnc_prefs_get_string(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR);
117  auto separator = gnc_normalize_account_separator (string);
118 
119  gnc_set_account_separator(separator);
120 
121  g_free(string);
122  g_free(separator);
123 }
124 
125 
126 static void
127 gnc_configure_reverse_balance (void)
128 {
129  for (auto i = 0; i < NUM_ACCOUNT_TYPES; i++)
130  reverse_type[i] = false;
131 
132  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP))
133  {
134  reverse_type[ACCT_TYPE_INCOME] = true;
135  reverse_type[ACCT_TYPE_EXPENSE] = true;
136  }
137  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT))
138  {
139  reverse_type[ACCT_TYPE_LIABILITY] = true;
140  reverse_type[ACCT_TYPE_PAYABLE] = true;
141  reverse_type[ACCT_TYPE_EQUITY] = true;
142  reverse_type[ACCT_TYPE_INCOME] = true;
143  reverse_type[ACCT_TYPE_CREDIT] = true;
144  }
145  else if (!gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE))
146  PWARN("no reversed account preference set, using none");
147 
148 }
149 
150 static void
151 gnc_reverse_balance_init (void)
152 {
153  gnc_configure_reverse_balance ();
154  reverse_balance_inited = TRUE;
155 }
156 
157 gboolean
158 gnc_reverse_balance (const Account *account)
159 {
160  if (account == nullptr)
161  return FALSE;
162 
163  auto type = xaccAccountGetType (account);
164  if ((type < 0) || (type >= NUM_ACCOUNT_TYPES))
165  return FALSE;
166 
167  if (!reverse_balance_inited)
168  gnc_reverse_balance_init ();
169 
170  return reverse_type[type];
171 }
172 
173 gboolean gnc_using_equity_type_opening_balance_account (QofBook* book)
174 {
175  return gnc_features_check_used (book, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE);
176 }
177 
178 void gnc_set_use_equity_type_opening_balance_account (QofBook* book)
179 {
180  gnc_features_set_used (book, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE);
181 }
182 
183 char*
184 gnc_get_default_directory (const char* section)
185 {
186  auto dir = gnc_prefs_get_string (section, GNC_PREF_LAST_PATH);
187  if (!(dir && *dir))
188  {
189  g_free (dir); // if it's ""
190 #ifdef G_OS_WIN32
191  dir = g_strdup (g_get_user_data_dir ()); /* equivalent of "My Documents" */
192 #else
193  dir = g_strdup (g_get_home_dir ());
194 #endif
195  }
196  return dir;
197 }
198 
199 void
200 gnc_set_default_directory (const char* section, const char* directory)
201 {
202  gnc_prefs_set_string(section, GNC_PREF_LAST_PATH, directory);
203 }
204 
205 QofBook *
206 gnc_get_current_book (void)
207 {
208  return qof_session_get_book (gnc_get_current_session ());
209 }
210 
211 /* If there is no current session, there is no book and we must be dealing
212  * with a new book. When gnucash is started with --nofile, there is
213  * initially no session (and no book), but by the time we check, one
214  * could have been created (for example, if an empty account tree tab is
215  * opened, a session is created which creates a new, but empty, book).
216  * A session is created and a book is loaded from a backend when gnucash is
217  * started with a file, but selecting 'new file' keeps a session open. So we
218  * need to check as well for a book with no accounts (root with no children). */
219 gboolean
220 gnc_is_new_book (void)
221 {
222  return (!gnc_current_session_exist() ||
223  gnc_account_n_children (gnc_book_get_root_account (gnc_get_current_book ())) == 0);
224 }
225 
226 #define OPTION_TAXUS_NAME "tax_US/name"
227 #define OPTION_TAXUS_TYPE "tax_US/type"
228 #define OLD_OPTION_TAXUS_NAME "book/tax_US/name"
229 #define OLD_OPTION_TAXUS_TYPE "book/tax_US/type"
230 
231 void
232 gnc_set_current_book_tax_name_type (gboolean name_changed, const char* tax_name,
233  gboolean type_changed, const char* tax_type)
234 {
235  if (name_changed)
236  {
237  if (type_changed)
238  {
239  auto book = gnc_get_current_book();
240  if ((g_strcmp0 (tax_name, "") == 0) ||
241  (tax_name == nullptr))
242  { /* change to no name */
243  if ((g_strcmp0 (tax_type, "Other") == 0) ||
244  (g_strcmp0 (tax_type, "") == 0) ||
245  (tax_type == nullptr))
246  { /* need to delete both name and type and the "tax_US" frame */
247  qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
248  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
249  qof_book_option_frame_delete(book, "tax_US");
250  }
251  else
252  { /* delete the name & change the type; keep the "tax_US" frame */
253  qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
254  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
255  }
256  }
257  else /* new name */
258  {
259  if ((g_strcmp0 (tax_type, "Other") == 0) ||
260  (g_strcmp0 (tax_type, "") == 0) ||
261  (tax_type == nullptr))
262  { /* delete the type & change the name; keep the "tax_US" frame */
263  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
264  qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
265  }
266  else /* and new type */
267  { /* change the name & change the type */
268  qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
269  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
270  }
271  }
272  }
273  else /* no type change but name changed */
274  {
275  auto book = gnc_get_current_book();
276  if ((g_strcmp0 (tax_name, "") == 0) ||
277  (tax_name == nullptr))
278  { /* change to no name */
279  if ((g_strcmp0 (tax_type, "Other") == 0) ||
280  (g_strcmp0 (tax_type, "") == 0) ||
281  (tax_type == nullptr))
282  { /* delete the name; there is no type; deleted the "tax_US" frame */
283  qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
284  qof_book_option_frame_delete(book, "tax_US");
285  }
286  else
287  { /* need to delete the name and keep "tax_US" frame */
288  qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
289  }
290  }
291  else
292  { /* change the name & keep "tax_US" frame */
293  qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
294  }
295  }
296  }
297  else /* no name change */
298  {
299  if (type_changed)
300  {
301  auto book = gnc_get_current_book();
302  if ((g_strcmp0 (tax_type, "Other") == 0) ||
303  (g_strcmp0 (tax_type, "") == 0) ||
304  (tax_type == nullptr))
305  {
306  if ((g_strcmp0 (tax_name, "") == 0) ||
307  (tax_name == nullptr))
308  {/* delete the type; there is no name; delete the "tax_US" frame */
309  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
310  qof_book_option_frame_delete(book, "tax_US");
311  }
312  else
313  { /* need to delete the type and keep "tax_US" frame */
314  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
315  }
316  }
317  else
318  { /* change the type & keep "tax_US" frame */
319  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
320  }
321  } /*else no name and no type change - do nothing */
322  }
323 }
324 
325 const char*
326 gnc_get_current_book_tax_name (void)
327 {
328  auto book = gnc_get_current_book();
329  auto tax_name = qof_book_get_string_option(book, OPTION_TAXUS_NAME);
330  if (tax_name)
331  {
332  return tax_name;
333  }
334  else
335  {
336  const char* old_option_taxus_name =
337  qof_book_get_string_option(book, OLD_OPTION_TAXUS_NAME);
338  if (old_option_taxus_name)
339  {
340  char* taxus_name = g_strdup(old_option_taxus_name);
341  const char* old_option_taxus_type =
342  qof_book_get_string_option(book, OLD_OPTION_TAXUS_TYPE);
343  if (old_option_taxus_type)
344  { /* switch both name and type and remove unused frames */
345  char* taxus_type = g_strdup(old_option_taxus_type);
346  qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
347  qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, nullptr);
348  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
349  qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, nullptr);
350  qof_book_option_frame_delete(book, "book/tax_US");
351  qof_book_option_frame_delete(book, "book");
352  g_free (taxus_type);
353  }
354  else
355  { /* switch just name and remove unused frames */
356  qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
357  qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, nullptr);
358  qof_book_option_frame_delete(book, "book/tax_US");
359  qof_book_option_frame_delete(book, "book");
360  }
361  g_free (taxus_name);
362  return qof_book_get_string_option(book, OPTION_TAXUS_NAME);
363  }
364  return nullptr;
365  }
366 }
367 
368 const char*
369 gnc_get_current_book_tax_type (void)
370 {
371  auto book = gnc_get_current_book();
372  auto tax_type =
373  qof_book_get_string_option(book, OPTION_TAXUS_TYPE);
374  if (tax_type)
375  {
376  return tax_type;
377  }
378  else
379  {
380  auto old_option_taxus_type =
381  qof_book_get_string_option(book, OLD_OPTION_TAXUS_TYPE);
382  if (old_option_taxus_type)
383  {
384  auto taxus_type = g_strdup(old_option_taxus_type);
385  auto old_option_taxus_name =
386  qof_book_get_string_option(book, OLD_OPTION_TAXUS_NAME);
387  if (old_option_taxus_name)
388  { /* switch both name and type and remove unused frames */
389  auto taxus_name = g_strdup(old_option_taxus_name);
390  qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
391  qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, nullptr);
392  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
393  qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, nullptr);
394  qof_book_option_frame_delete(book, "book/tax_US");
395  qof_book_option_frame_delete(book, "book");
396  g_free (taxus_name);
397  }
398  else
399  { /* switch just type and remove unused frames */
400  qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
401  qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, nullptr);
402  qof_book_option_frame_delete(book, "book/tax_US");
403  qof_book_option_frame_delete(book, "book");
404  }
405  g_free (taxus_type);
406  return qof_book_get_string_option(book, OPTION_TAXUS_TYPE);
407  }
408  return nullptr;
409  }
410 }
411 
412 Account *
413 gnc_get_current_root_account (void)
414 {
415  return gnc_book_get_root_account (gnc_get_current_book ());
416 }
417 
418 gnc_commodity_table *
419 gnc_get_current_commodities (void)
420 {
421  if (gnc_current_session_exist())
422  return gnc_commodity_table_get_table (gnc_get_current_book ());
423  return nullptr;
424 }
425 
426 char*
428  gboolean show_leaf_accounts)
429 {
430  if (show_leaf_accounts)
431  return g_strdup (xaccAccountGetName (account));
432  else
433  return gnc_account_get_full_name (account);
434 }
435 
436 char*
438 {
439  auto show_leaf_accounts =
440  gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
441  GNC_PREF_SHOW_LEAF_ACCT_NAMES);
442 
443  return gnc_get_account_name_for_split_register(account, show_leaf_accounts);
444 }
445 
446 Account *
447 gnc_account_lookup_for_register(const Account *base_account, const char* name)
448 {
449  auto show_leaf_accounts =
450  gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
451  GNC_PREF_SHOW_LEAF_ACCT_NAMES);
452 
453  if (show_leaf_accounts)
454  return gnc_account_lookup_by_name (base_account, name);
455  else
456  return gnc_account_lookup_by_full_name (base_account, name);
457 }
458 
459 /********************************************************************\
460  * gnc_get_reconcile_str *
461  * return the i18n'd string for the given reconciled flag *
462  * *
463  * Args: reconciled_flag - the flag to convert into a string *
464  * Returns: the i18n'd reconciled string *
465 \********************************************************************/
466 const char*
467 gnc_get_reconcile_str (char reconciled_flag)
468 {
469  switch (reconciled_flag)
470  {
471  case NREC:
472  return C_("Reconciled flag 'not cleared'", "n");
473  case CREC:
474  return C_("Reconciled flag 'cleared'", "c");
475  case YREC:
476  return C_("Reconciled flag 'reconciled'", "y");
477  case FREC:
478  return C_("Reconciled flag 'frozen'", "f");
479  case VREC:
480  return C_("Reconciled flag 'void'", "v");
481  default:
482  PERR("Bad reconciled flag\n");
483  return nullptr;
484  }
485 }
486 
487 /********************************************************************\
488  * gnc_get_reconcile_valid_flags *
489  * return a string containing the list of reconciled flags *
490  * *
491  * Returns: the i18n'd reconciled flags string *
492 \********************************************************************/
493 const char*
494 gnc_get_reconcile_valid_flags (void)
495 {
496  static const char flags[] = { NREC, CREC, YREC, FREC, VREC, 0 };
497  return flags;
498 }
499 
500 /********************************************************************\
501  * gnc_get_reconcile_flag_order *
502  * return a string containing the reconciled-flag change order *
503  * *
504  * Args: reconciled_flag - the flag to convert into a string *
505  * Returns: the i18n'd reconciled string *
506 \********************************************************************/
507 const char*
508 gnc_get_reconcile_flag_order (void)
509 {
510  static const char flags[] = { NREC, CREC, 0 };
511  return flags;
512 }
513 
514 const char*
515 gnc_get_doclink_str (char link_flag)
516 {
517  switch (link_flag)
518  {
519  case WLINK:
520  return C_("Document Link flag for 'web'", "w");
521  case FLINK:
522  return C_("Document Link flag for 'file'", "f");
523  case ' ':
524  return " ";
525  default:
526  PERR("Bad link flag");
527  return nullptr;
528  }
529 }
530 
531 const char*
533 {
534  static const char flags[] = { FLINK, WLINK, ' ', 0 };
535  return flags;
536 }
537 
538 const char*
540 {
541  static const char flags[] = { FLINK, WLINK, ' ', 0 };
542  return flags;
543 }
544 
545 static const char*
546 equity_base_name (GNCEquityType equity_type)
547 {
548  switch (equity_type)
549  {
550  case EQUITY_OPENING_BALANCE:
551  return N_("Opening Balances");
552 
553  case EQUITY_RETAINED_EARNINGS:
554  return N_("Retained Earnings");
555 
556  default:
557  return nullptr;
558  }
559 }
560 
561 Account *
562 gnc_find_or_create_equity_account (Account *root,
563  GNCEquityType equity_type,
564  gnc_commodity *currency)
565 {
566  g_return_val_if_fail (equity_type >= 0, nullptr);
567  g_return_val_if_fail (equity_type < NUM_EQUITY_TYPES, nullptr);
568  g_return_val_if_fail (currency != nullptr, nullptr);
569  g_return_val_if_fail (root != nullptr, nullptr);
570  g_return_val_if_fail (gnc_commodity_is_currency(currency), nullptr);
571 
572  auto use_eq_op_feature = equity_type == EQUITY_OPENING_BALANCE && gnc_using_equity_type_opening_balance_account (gnc_get_current_book ());
573 
574  if (use_eq_op_feature)
575  {
576  auto account = gnc_account_lookup_by_opening_balance (root, currency);
577  if (account)
578  return account;
579  }
580 
581  auto base_name = equity_base_name (equity_type);
582 
583  auto account = gnc_account_lookup_by_name(root, base_name);
584  if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
585  account = nullptr;
586 
587  if (!account)
588  {
589  base_name = base_name && *base_name ? _(base_name) : "";
590 
591  account = gnc_account_lookup_by_name(root, base_name);
592  if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
593  account = nullptr;
594  }
595 
596  auto base_name_exists = (account != nullptr);
597 
598  if (account &&
599  gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
600  {
601  if (use_eq_op_feature)
602  xaccAccountSetIsOpeningBalance (account, TRUE);
603  return account;
604  }
605 
606  auto name = g_strconcat (base_name, " - ",
607  gnc_commodity_get_mnemonic (currency), nullptr);
608  account = gnc_account_lookup_by_name(root, name);
609  if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
610  account = nullptr;
611 
612  auto name_exists = (account != nullptr);
613 
614  if (account &&
615  gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
616  {
617  if (use_eq_op_feature)
618  xaccAccountSetIsOpeningBalance (account, TRUE);
619  return account;
620  }
621 
622  /* Couldn't find one, so create it */
623  if (name_exists && base_name_exists)
624  {
625  PWARN ("equity account with unexpected currency");
626  g_free (name);
627  return nullptr;
628  }
629 
630  if (!base_name_exists &&
632  {
633  g_free (name);
634  name = g_strdup (base_name);
635  }
636 
637  auto parent = gnc_account_lookup_by_name(root, _("Equity"));
638  if (!parent || xaccAccountGetType (parent) != ACCT_TYPE_EQUITY)
639  parent = root;
640  g_assert(parent);
641 
642  account = xaccMallocAccount (gnc_account_get_book(root));
643 
644  xaccAccountBeginEdit (account);
645 
646  xaccAccountSetName (account, name);
648  xaccAccountSetCommodity (account, currency);
649 
650  if (use_eq_op_feature)
651  xaccAccountSetIsOpeningBalance (account, TRUE);
652 
653  xaccAccountBeginEdit (parent);
654  gnc_account_append_child (parent, account);
655  xaccAccountCommitEdit (parent);
656 
657  xaccAccountCommitEdit (account);
658 
659  g_free (name);
660 
661  return account;
662 }
663 
664 gboolean
665 gnc_account_create_opening_balance (Account *account,
666  gnc_numeric balance,
667  time64 date,
668  QofBook *book)
669 {
670  if (gnc_numeric_zero_p (balance))
671  return TRUE;
672 
673  g_return_val_if_fail (account != nullptr, FALSE);
674  auto commodity = xaccAccountGetCommodity (account);
675  g_return_val_if_fail (gnc_commodity_is_currency (commodity), FALSE);
676 
677  auto equity_account =
678  gnc_find_or_create_equity_account (gnc_account_get_root(account),
679  EQUITY_OPENING_BALANCE,
680  commodity);
681  if (!equity_account)
682  return FALSE;
683 
684  xaccAccountBeginEdit (account);
685  xaccAccountBeginEdit (equity_account);
686 
687  auto trans = xaccMallocTransaction (book);
688 
689  xaccTransBeginEdit (trans);
690 
691  xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, nullptr));
693  xaccTransSetDescription (trans, _("Opening Balance"));
694 
695  auto split = xaccMallocSplit (book);
696 
697  xaccTransAppendSplit (trans, split);
698  xaccAccountInsertSplit (account, split);
699 
700  xaccSplitSetAmount (split, balance);
701  xaccSplitSetValue (split, balance);
702 
703  balance = gnc_numeric_neg (balance);
704 
705  split = xaccMallocSplit (book);
706 
707  xaccTransAppendSplit (trans, split);
708  xaccAccountInsertSplit (equity_account, split);
709 
710  xaccSplitSetAmount (split, balance);
711  xaccSplitSetValue (split, balance);
712 
713  xaccTransCommitEdit (trans);
714  xaccAccountCommitEdit (equity_account);
715  xaccAccountCommitEdit (account);
716 
717  return TRUE;
718 }
719 
720 
721 gnc_commodity *
723 {
724  auto table = gnc_get_current_commodities ();
725  auto code = gnc_locale_default_iso_currency_code ();
726 
727  auto currency = gnc_commodity_table_lookup (table,
728  GNC_COMMODITY_NS_CURRENCY,
729  code);
730 
731  return (currency ? currency : nullptr);
732 }
733 
734 gnc_commodity*
736 {
737  auto currency = gnc_locale_default_currency_nodefault ();
738 
739  return (currency ? currency :
740  gnc_commodity_table_lookup (gnc_get_current_commodities (),
741  GNC_COMMODITY_NS_CURRENCY, "USD"));
742 }
743 
744 
745 static gnc_commodity*
746 gnc_default_currency_common (char* requested_currency,
747  const char* section)
748 {
749  gnc_commodity *currency = nullptr;
750 
751  if (requested_currency)
752  return gnc_commodity_table_lookup(gnc_get_current_commodities(),
753  GNC_COMMODITY_NS_CURRENCY,
754  requested_currency);
755 
756  if (gnc_current_session_exist() &&
757  gnc_prefs_get_bool (section, GNC_PREF_CURRENCY_CHOICE_OTHER))
758  {
759  auto mnemonic = gnc_prefs_get_string(section, GNC_PREF_CURRENCY_OTHER);
760  currency = gnc_commodity_table_lookup(gnc_get_current_commodities(),
761  GNC_COMMODITY_NS_CURRENCY,
762  mnemonic);
763  DEBUG("mnemonic %s, result %p",
764  mnemonic && *mnemonic ? mnemonic : "(null)", currency);
765  g_free(mnemonic);
766  }
767 
768  if (!currency)
769  currency = gnc_locale_default_currency ();
770 
771  if (currency)
772  {
773  g_free (requested_currency);
774  }
775 
776  return currency;
777 }
778 
779 gnc_commodity*
781 {
782  return gnc_default_currency_common (user_default_currency,
783  GNC_PREFS_GROUP_GENERAL);
784 }
785 
786 gnc_commodity*
788  gboolean * currency_from_account_found)
789 {
790  gnc_commodity *currency;
791  if (!account)
792  {
793  if (currency_from_account_found)
794  *currency_from_account_found = FALSE;
795  return gnc_default_currency();
796  }
797 
798  currency = gnc_account_get_currency_or_parent(account);
799  if (currency)
800  {
801  if (currency_from_account_found)
802  *currency_from_account_found = TRUE;
803  }
804  else
805  {
806  if (currency_from_account_found)
807  *currency_from_account_found = FALSE;
808  currency = gnc_default_currency();
809  }
810  return currency;
811 }
812 
813 
814 
815 gnc_commodity*
817 {
818  return gnc_default_currency_common (user_report_currency,
819  GNC_PREFS_GROUP_GENERAL_REPORT);
820 }
821 
822 static void
823 gnc_currency_changed_cb (GSettings *settings, char* key, gpointer user_data)
824 {
825  user_default_currency = nullptr;
826  user_report_currency = nullptr;
827  gnc_hook_run(HOOK_CURRENCY_CHANGED, nullptr);
828 }
829 
830 
832 gnc_default_print_info (gboolean use_symbol)
833 {
834  static GNCPrintAmountInfo info;
835  static bool got_it = false;
836 
837  /* These must be updated each time. */
838  info.use_symbol = use_symbol ? 1 : 0;
839  info.commodity = gnc_default_currency ();
840 
841  if (got_it)
842  return info;
843 
844  auto lc = gnc_localeconv ();
845 
846  info.max_decimal_places = lc->frac_digits;
847  info.min_decimal_places = lc->frac_digits;
848 
849  info.use_separators = 1;
850  info.use_locale = 1;
851  info.monetary = 1;
852  info.force_fit = 0;
853  info.round = 0;
854 
855  got_it = TRUE;
856 
857  return info;
858 }
859 
860 static bool
861 is_decimal_fraction (int fraction, uint8_t *max_decimal_places_p)
862 {
863  uint8_t max_decimal_places = 0;
864 
865  if (fraction <= 0)
866  return false;
867 
868  while (fraction != 1)
869  {
870  if (fraction % 10 != 0)
871  return false;
872 
873  fraction = fraction / 10;
874  max_decimal_places += 1;
875  }
876 
877  if (max_decimal_places_p)
878  *max_decimal_places_p = max_decimal_places;
879 
880  return true;
881 }
882 
884 gnc_commodity_print_info (const gnc_commodity *commodity,
885  gboolean use_symbol)
886 {
887  GNCPrintAmountInfo info;
888 
889  if (commodity == nullptr)
890  return gnc_default_print_info (use_symbol);
891 
892  info.commodity = commodity;
893 
894  auto is_iso = gnc_commodity_is_iso (commodity);
895 
896  if (is_decimal_fraction (gnc_commodity_get_fraction (commodity),
897  &info.max_decimal_places))
898  {
899  if (is_iso)
900  info.min_decimal_places = info.max_decimal_places;
901  else
902  info.min_decimal_places = 0;
903  }
904  else
905  info.max_decimal_places = info.min_decimal_places = 0;
906 
907  info.use_separators = 1;
908  info.use_symbol = use_symbol ? 1 : 0;
909  info.use_locale = is_iso ? 1 : 0;
910  info.monetary = 1;
911  info.force_fit = 0;
912  info.round = 0;
913 
914  return info;
915 }
916 
917 static GNCPrintAmountInfo
918 gnc_account_print_info_helper(const Account *account, gboolean use_symbol,
919  gnc_commodity * (*efffunc)(const Account *),
920  int (*scufunc)(const Account*))
921 {
922  GNCPrintAmountInfo info;
923 
924  if (account == nullptr)
925  return gnc_default_print_info (use_symbol);
926 
927  info.commodity = efffunc (account);
928 
929  auto is_iso = gnc_commodity_is_iso (info.commodity);
930 
931  auto scu = scufunc (account);
932 
933  if (is_decimal_fraction (scu, &info.max_decimal_places))
934  {
935  if (is_iso)
936  info.min_decimal_places = info.max_decimal_places;
937  else
938  info.min_decimal_places = 0;
939  }
940  else
941  info.max_decimal_places = info.min_decimal_places = 0;
942 
943  info.use_separators = 1;
944  info.use_symbol = use_symbol ? 1 : 0;
945  info.use_locale = is_iso ? 1 : 0;
946  info.monetary = 1;
947  info.force_fit = 0;
948  info.round = 0;
949 
950  return info;
951 }
952 
954 gnc_account_print_info (const Account *account, gboolean use_symbol)
955 {
956  return gnc_account_print_info_helper(account, use_symbol,
959 }
960 
962 gnc_split_amount_print_info (Split *split, gboolean use_symbol)
963 {
964  if (!split)
965  {
966  GNCPrintAmountInfo info = gnc_default_share_print_info ();
967  info.use_symbol = use_symbol;
968  return info;
969  }
970 
971  return gnc_account_print_info (xaccSplitGetAccount (split), use_symbol);
972 }
973 
974 static GNCPrintAmountInfo
975 gnc_default_print_info_helper (int decplaces)
976 {
977  GNCPrintAmountInfo info;
978 
979  info.commodity = nullptr;
980 
981  info.max_decimal_places = decplaces;
982  info.min_decimal_places = 0;
983 
984  info.use_separators = 1;
985  info.use_symbol = 0;
986  info.use_locale = 1;
987  info.monetary = 1;
988  info.force_fit = 0;
989  info.round = 0;
990 
991  return info;
992 }
993 
995 gnc_default_share_print_info (void)
996 {
997  static GNCPrintAmountInfo info;
998  static bool got_it = false;
999 
1000  if (!got_it)
1001  {
1002  info = gnc_default_print_info_helper (5);
1003  info.monetary = 0;
1004  got_it = TRUE;
1005  }
1006 
1007  return info;
1008 }
1009 
1011 gnc_share_print_info_places (int decplaces)
1012 {
1013  GNCPrintAmountInfo info;
1014 
1015  info = gnc_default_share_print_info ();
1016  info.max_decimal_places = decplaces;
1017  info.min_decimal_places = decplaces;
1018  info.force_fit = 1;
1019  info.round = 1;
1020  return info;
1021 }
1022 
1024 gnc_price_print_info (const gnc_commodity *curr, gboolean use_symbol)
1025 {
1026  GNCPrintAmountInfo info;
1027  auto force = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
1028  GNC_PREF_PRICES_FORCE_DECIMAL);
1029  info.commodity = curr;
1030 
1031  if (info.commodity)
1032  {
1033  int frac = gnc_commodity_get_fraction (curr);
1034  guint8 decplaces = 2;
1035  while (frac != 1 && (frac % 10) == 0 && (frac /= 10)) ++decplaces;
1036  info.max_decimal_places = decplaces;
1037  info.min_decimal_places = decplaces;
1038  }
1039  else
1040  {
1041  info.max_decimal_places = 6;
1042  info.min_decimal_places = 0;
1043  }
1044 
1045  info.use_separators = 1;
1046  info.use_symbol = use_symbol ? 1 : 0;
1047  info.use_locale = 1;
1048  info.monetary = 1;
1049 
1050  info.force_fit = force;
1051  info.round = force;
1052  return info;
1053 }
1054 
1056 gnc_default_price_print_info (const gnc_commodity *curr)
1057 {
1058  return gnc_price_print_info (curr, FALSE);
1059 }
1060 
1061 
1063 gnc_integral_print_info (void)
1064 {
1065  static GNCPrintAmountInfo info;
1066  static bool got_it = false;
1067 
1068  if (!got_it)
1069  {
1070  info = gnc_default_print_info_helper (0);
1071  got_it = TRUE;
1072  }
1073 
1074  return info;
1075 }
1076 
1077 /* Utility function for printing non-negative amounts */
1078 static int
1079 PrintAmountInternal(char* buf, gnc_numeric val, const GNCPrintAmountInfo *info)
1080 {
1081  auto *lc = gnc_localeconv();
1082  constexpr size_t buf_size = 128;
1083  char temp_buf[buf_size];
1084 
1085  g_return_val_if_fail (info != nullptr, 0);
1086 
1087  if (gnc_numeric_check (val))
1088  {
1089  PWARN ("Bad numeric: %s.",
1091  *buf = '\0';
1092  return 0;
1093  }
1094 
1095  /* Print the absolute value, but remember sign */
1096  auto value_is_negative = gnc_numeric_negative_p (val);
1097  val = gnc_numeric_abs (val);
1098 
1099  /* Try to print as decimal. */
1100  auto value_is_decimal = gnc_numeric_to_decimal(&val, nullptr);
1101  if (!value_is_decimal && info->force_fit && info->round)
1102  {
1103  /* if there's a commodity use 100x the commodity's fraction. N.B. This
1104  * assumes that commodity fractions are multiples of 10, a reasonable
1105  * assumption in 2018. Otherwise, if there's a reasonable
1106  * max_decimal_places, use that.
1107  */
1108  const int64_t denom = info->commodity ?
1109  gnc_commodity_get_fraction(info->commodity) * 100 :
1110  (info->max_decimal_places &&
1111  info->max_decimal_places <= maximum_decimals) ?
1112  pow_10[info->max_decimal_places] : pow_10[maximum_decimals];
1114  value_is_decimal = gnc_numeric_to_decimal(&val, nullptr);
1115  }
1116  auto min_dp = info->min_decimal_places;
1117  auto max_dp = info->max_decimal_places;
1118 
1119  /* Don to limit the number of decimal places _UNLESS_ force_fit is
1120  * true. */
1121  if (!info->force_fit)
1122  max_dp = 99;
1123 
1124  /* rounding? -- can only ROUND if force_fit is also true */
1125  if (value_is_decimal && info->round && info->force_fit)
1126  {
1127  /* Limit the denom to 10^13 ~= 2^44, leaving max at ~524288 */
1128  gnc_numeric rounding{5, pow_10[max_dp + 1]};
1129 
1130  val = gnc_numeric_add(val, rounding, val.denom,
1132 
1133  if (gnc_numeric_check(val))
1134  {
1135  PWARN ("Bad numeric from rounding: %s.",
1137  *buf = '\0';
1138  return 0;
1139  }
1140  }
1141 
1142  /* calculate the integer part and the remainder */
1143  auto whole = gnc_numeric_convert(val, 1, GNC_HOW_RND_TRUNC);
1144  val = gnc_numeric_sub (val, whole, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
1145  if (gnc_numeric_check (val))
1146  {
1147  PWARN ("Problem with remainder: %s.",
1149  *buf = '\0';
1150  return 0;
1151  }
1152 
1153  // Value may now be decimal, for example if the factional part is zero
1154  value_is_decimal = gnc_numeric_to_decimal(&val, nullptr);
1155  /* print the integer part without separators */
1156  snprintf(temp_buf, buf_size, "%" G_GINT64_FORMAT, whole.num);
1157  auto num_whole_digits = strlen (temp_buf);
1158 
1159  if (!info->use_separators)
1160  strcpy (buf, temp_buf);
1161  else
1162  {
1163  char* separator;
1164  char* group;
1165 
1166  if (info->monetary)
1167  {
1168  separator = lc->mon_thousands_sep;
1169  group = lc->mon_grouping;
1170  }
1171  else
1172  {
1173  separator = lc->thousands_sep;
1174  group = lc->grouping;
1175  }
1176 
1177  auto buf_ptr = buf;
1178  auto temp_ptr = &temp_buf[num_whole_digits - 1];
1179  auto group_count = 0;
1180 
1181  while (temp_ptr != temp_buf)
1182  {
1183  *buf_ptr++ = *temp_ptr--;
1184 
1185  if (*group != CHAR_MAX)
1186  {
1187  group_count++;
1188 
1189  if (group_count == *group)
1190  {
1191  g_utf8_strncpy(buf_ptr, separator, 1);
1192  buf_ptr = g_utf8_find_next_char(buf_ptr, nullptr);
1193  group_count = 0;
1194 
1195  /* Peek ahead at the next group code */
1196  switch (group[1])
1197  {
1198  /* A null char means repeat the last group indefinitely */
1199  case '\0':
1200  break;
1201  /* CHAR_MAX means no more grouping allowed */
1202  case CHAR_MAX:
1203  /* fall through */
1204  /* Anything else means another group size */
1205  default:
1206  group++;
1207  break;
1208  }
1209  }
1210  }
1211  }
1212 
1213  /* We built the string backwards, now reverse */
1214  *buf_ptr++ = *temp_ptr;
1215  *buf_ptr = '\0';
1216  auto rev_buf = g_utf8_strreverse(buf, -1);
1217  strcpy (buf, rev_buf);
1218  g_free(rev_buf);
1219  } /* endif */
1220 
1221  /* at this point, buf contains the whole part of the number */
1222 
1223  /* If it's not decimal, print the fraction as an expression. */
1224  if (!value_is_decimal)
1225  {
1226  val = gnc_numeric_reduce (val);
1227 
1228  if (val.denom > 0)
1229  snprintf (temp_buf, buf_size, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
1230  val.num, val.denom);
1231  else
1232  snprintf (temp_buf, buf_size, "%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT,
1233  val.num, -val.denom);
1234 
1235  if (whole.num == 0)
1236  *buf = '\0';
1237  else if (value_is_negative)
1238  strcat(buf, " - ");
1239  else
1240  strcat(buf, " + ");
1241 
1242  strcat (buf, temp_buf);
1243  }
1244  else
1245  {
1246  guint8 num_decimal_places = 0;
1247  char* temp_ptr = temp_buf;
1248 
1249  auto decimal_point = info->monetary
1250  ? lc->mon_decimal_point
1251  : lc->decimal_point;
1252  g_utf8_strncpy(temp_ptr, decimal_point, 1);
1253  temp_ptr = g_utf8_find_next_char(temp_ptr, nullptr);
1254 
1255  while (!gnc_numeric_zero_p (val)
1256  && (val.denom != 1)
1257  && (num_decimal_places < max_dp))
1258  {
1259  gint64 digit;
1260 
1261  val.denom = val.denom / 10;
1262 
1263  digit = val.num / val.denom;
1264 
1265  *temp_ptr++ = digit + '0';
1266  num_decimal_places++;
1267 
1268  val.num = val.num - (digit * val.denom);
1269  }
1270 
1271  while (num_decimal_places < min_dp)
1272  {
1273  *temp_ptr++ = '0';
1274  num_decimal_places++;
1275  }
1276 
1277  /* cap the end and move to the last character */
1278  *temp_ptr-- = '\0';
1279 
1280  /* Here we strip off trailing decimal zeros per the argument. */
1281  while (*temp_ptr == '0' && num_decimal_places > min_dp)
1282  {
1283  *temp_ptr-- = '\0';
1284  num_decimal_places--;
1285  }
1286 
1287  if (num_decimal_places > max_dp)
1288  {
1289  PWARN ("max_decimal_places too small; limit %d, value %s%s",
1290  info->max_decimal_places, buf, temp_buf);
1291  }
1292 
1293  if (num_decimal_places > 0)
1294  strcat (buf, temp_buf);
1295  }
1296 
1297  return strlen(buf);
1298 }
1299 
1303 int
1304 xaccSPrintAmount (char* bufp, gnc_numeric val, GNCPrintAmountInfo info)
1305 {
1306  auto orig_bufp = bufp;
1307  auto currency_symbol = "";
1308  const char* sign;
1309 
1310  char cs_precedes;
1311  char sep_by_space;
1312  char sign_posn;
1313 
1314  bool print_sign = true;
1315  bool print_absolute = false;
1316 
1317  if (!bufp)
1318  return 0;
1319 
1320  auto lc = gnc_localeconv();
1321  if (info.use_locale)
1322  if (gnc_numeric_negative_p (val))
1323  {
1324  cs_precedes = lc->n_cs_precedes;
1325  sep_by_space = lc->n_sep_by_space;
1326  }
1327  else
1328  {
1329  cs_precedes = lc->p_cs_precedes;
1330  sep_by_space = lc->p_sep_by_space;
1331  }
1332  else
1333  {
1334  cs_precedes = TRUE;
1335  sep_by_space = TRUE;
1336  }
1337 
1338  if (info.commodity && info.use_symbol)
1339  {
1340  currency_symbol = gnc_commodity_get_nice_symbol (info.commodity);
1341  if (!gnc_commodity_is_iso (info.commodity))
1342  {
1343  cs_precedes = FALSE;
1344  sep_by_space = TRUE;
1345  }
1346  }
1347 
1348  if (gnc_numeric_negative_p (val))
1349  {
1350  sign = lc->negative_sign;
1351  sign_posn = lc->n_sign_posn;
1352  }
1353  else
1354  {
1355  sign = lc->positive_sign;
1356  sign_posn = lc->p_sign_posn;
1357  }
1358 
1359  if (gnc_numeric_zero_p (val) || (sign == nullptr) || (sign[0] == 0))
1360  print_sign = FALSE;
1361 
1362  /* See if we print sign now */
1363  if (print_sign && (sign_posn == 1))
1364  bufp = g_stpcpy(bufp, sign);
1365 
1366  /* Now see if we print currency */
1367  if (cs_precedes)
1368  {
1369  /* See if we print sign now */
1370  if (print_sign && (sign_posn == 3))
1371  bufp = g_stpcpy(bufp, sign);
1372 
1373  if (info.use_symbol)
1374  {
1375  bufp = g_stpcpy(bufp, currency_symbol);
1376  if (sep_by_space)
1377  bufp = g_stpcpy(bufp, " ");
1378  }
1379 
1380  /* See if we print sign now */
1381  if (print_sign && (sign_posn == 4))
1382  bufp = g_stpcpy(bufp, sign);
1383  }
1384 
1385  /* Now see if we print parentheses */
1386  if (print_sign && (sign_posn == 0))
1387  {
1388  bufp = g_stpcpy(bufp, "(");
1389  print_absolute = TRUE;
1390  }
1391 
1392  /* Now print the value */
1393  bufp += PrintAmountInternal(bufp,
1394  print_absolute ? gnc_numeric_abs(val) : val,
1395  &info);
1396 
1397  /* Now see if we print parentheses */
1398  if (print_sign && (sign_posn == 0))
1399  bufp = g_stpcpy(bufp, ")");
1400 
1401  /* Now see if we print currency */
1402  if (!cs_precedes)
1403  {
1404  /* See if we print sign now */
1405  if (print_sign && (sign_posn == 3))
1406  bufp = g_stpcpy(bufp, sign);
1407 
1408  if (info.use_symbol)
1409  {
1410  if (sep_by_space)
1411  bufp = g_stpcpy(bufp, " ");
1412  bufp = g_stpcpy(bufp, currency_symbol);
1413  }
1414 
1415  /* See if we print sign now */
1416  if (print_sign && (sign_posn == 4))
1417  bufp = g_stpcpy(bufp, sign);
1418  }
1419 
1420  /* See if we print sign now */
1421  if (print_sign && (sign_posn == 2))
1422  bufp = g_stpcpy(bufp, sign);
1423 
1424  /* return length of printed string */
1425  return (bufp - orig_bufp);
1426 }
1427 
1428 #define BUFLEN 1024
1429 
1430 const char*
1431 xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info)
1432 {
1433  /* hack alert -- this is not thread safe ... */
1434  static char buf[BUFLEN];
1435 
1436  if (!xaccSPrintAmount (buf, val, info))
1437  buf[0] = '\0';
1438 
1439  /* its OK to return buf, since we declared it static */
1440  return buf;
1441 }
1442 
1443 const char*
1445 {
1446  /* hack alert -- this is not thread safe ... */
1447  static char buf[BUFLEN];
1448  static const char ltr_isolate[] = { '\xe2', '\x81', '\xa6' };
1449  static const char ltr_pop_isolate[] = { '\xe2', '\x81', '\xa9' };
1450  auto offset = info.use_symbol ? 3 : 0;
1451 
1452  if (!gnc_commodity_is_currency (info.commodity))
1453  offset = 0;
1454 
1455  memset (buf, 0, BUFLEN);
1456  if (!xaccSPrintAmount (buf + offset, val, info))
1457  {
1458  buf[0] = '\0';
1459  return buf;
1460  };
1461 
1462  if (offset == 0)
1463  return buf;
1464 
1465  memcpy (buf, ltr_isolate, 3);
1466 
1467  if (buf[BUFLEN - 4] == '\0')
1468  {
1469  auto length = strlen (buf);
1470  memcpy (buf + length, ltr_pop_isolate, 3);
1471  }
1472  else
1473  {
1474  buf[BUFLEN - 1] = '\0';
1475  memcpy (buf + BUFLEN - 4, ltr_pop_isolate, 3);
1476 
1477  PWARN("buffer length %d exceeded, string truncated was %s", BUFLEN, buf);
1478  }
1479  /* its OK to return buf, since we declared it static
1480  and is immediately g_strdup'd */
1481  return buf;
1482 }
1483 
1484 char*
1486 {
1487  static const char* ltr = "\u2066"; // ltr isolate
1488  static const char* pop = "\u2069"; // pop directional formatting
1489 
1490  if (!text)
1491  return nullptr;
1492 
1493  return g_strconcat (ltr, text, pop, nullptr);
1494 }
1495 
1496 /********************************************************************\
1497  ********************************************************************/
1498 
1499 #define FUDGE .00001
1500 
1501 /* This function is basically untranslatable. I'd
1502  guess out of the 29 translations we have, 20 will have their number
1503  wordings in a totally different way than English has (not to
1504  mention gender-dependent number endings). Which means this
1505  word-by-word translation will be useless or even plain
1506  wrong. For this reason, we don't even start to pretend a
1507  word-by-word translation would be of any use, so we don't mark any
1508  of these strings for translation. cstim, 2007-04-15. */
1509 static const char* small_numbers[] =
1510 {
1511  /* Translators: This section is for generating the "amount, in
1512  words" field when printing a check. This function gets the
1513  wording right for English, but unfortunately not for most other
1514  languages. Decide for yourself whether the check printing is
1515  actually needed in your language; if not, you can safely skip the
1516  translation of all of these strings. */
1517  "Zero", "One", "Two", "Three", "Four",
1518  "Five", "Six", "Seven", "Eight", "Nine",
1519  "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
1520  "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen",
1521  "Twenty"
1522 };
1523 static const char* medium_numbers[] =
1524 {
1525  "Zero", "Ten", "Twenty", "Thirty", "Forty",
1526  "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"
1527 };
1528 static const char* big_numbers[] =
1529 {
1530  /* Translators: This is the word for the number 10^2 */
1531  "Hundred",
1532  /* Translators: This is the word for the number 10^3 */
1533  "Thousand",
1534  /* Translators: This is the word for the number 10^6, one thousand
1535  thousands. */
1536  "Million",
1537  /* Translators: This is the word for the number 10^9, one thousand
1538  millions. WATCH OUT: In British English and many other languages
1539  this word is used for 10^12 which is one million millions! In
1540  contrast to this, here in GnuCash this is used in the American
1541  English meaning of 10^9. */
1542  "Billion",
1543  /* Translators: This is the word for the number 10^12, one million
1544  millions. */
1545  "Trillion",
1546  /* Translators: This is the word for the number 10^15 */
1547  "Quadrillion",
1548  /* Translators: This is the word for the number 10^18 */
1549  "Quintillion"
1550 };
1551 
1552 static char*
1553 integer_to_words(gint64 val)
1554 {
1555  if (val == 0)
1556  return g_strdup("zero");
1557  if (val < 0)
1558  val = -val;
1559 
1560  auto result = g_string_sized_new(100);
1561 
1562  while (val >= 1000)
1563  {
1564  int log_val = log10(val) / 3 + FUDGE;
1565  int pow_val = exp(log_val * 3 * G_LN10) + FUDGE;
1566  int this_part = val / pow_val;
1567  val -= this_part * pow_val;
1568  auto tmp = integer_to_words(this_part);
1569  g_string_append_printf(result, "%s %s ", tmp, gettext(big_numbers[log_val]));
1570  g_free(tmp);
1571  }
1572 
1573  if (val >= 100)
1574  {
1575  int this_part = val / 100;
1576  val -= this_part * 100;
1577  g_string_append_printf(result, "%s %s ",
1578  gettext(small_numbers[this_part]),
1579  gettext(big_numbers[0]));
1580  }
1581 
1582  if (val > 20)
1583  {
1584  int this_part = val / 10;
1585  val -= this_part * 10;
1586  g_string_append(result, gettext(medium_numbers[this_part]));
1587  g_string_append_c(result, ' ');
1588  }
1589 
1590  if (val > 0)
1591  {
1592  int this_part = val;
1593  g_string_append(result, gettext(small_numbers[this_part]));
1594  g_string_append_c(result, ' ');
1595  }
1596 
1597  result = g_string_truncate(result, result->len - 1);
1598  return g_string_free(result, FALSE);
1599 }
1600 
1601 #ifdef _MSC_VER
1602 static double round(double x)
1603 {
1604  // A simple round() implementation because MSVC doesn't seem to have that
1605  return floor(x + 0.5);
1606 }
1607 #endif
1608 
1609 char*
1610 number_to_words(double val, int64_t denom)
1611 {
1612  if (val < 0) val = -val;
1613  if (denom < 0) denom = -denom;
1614 
1615  auto int_part = floor(val);
1616  auto frac_part = static_cast<int64_t>(round((val - int_part) * denom));
1617 
1618  auto int_string = integer_to_words(int_part);
1619  /* Inside of the gettext macro _(...) we must not use any macros but
1620  only plain string literals. For this reason, convert the strings
1621  separately. */
1622  auto nomin_string = g_strdup_printf("%02" PRId64, frac_part);
1623  auto denom_string = g_strdup_printf("%" PRId64, denom);
1624  auto full_string =
1625  /* Translators: This is for the "amount, in words" field in check
1626  printing. The first %s is the integer amount of dollars (or
1627  whatever currency), the second and third %s the cent amount as
1628  a fraction, e.g. 47/100. */
1629  g_strdup_printf("%s and %s/%s",
1630  int_string, nomin_string, denom_string);
1631  g_free(int_string);
1632  g_free(nomin_string);
1633  g_free(denom_string);
1634  return full_string;
1635 }
1636 
1637 char*
1638 numeric_to_words(gnc_numeric val)
1639 {
1640  return number_to_words(gnc_numeric_to_double(val),
1641  gnc_numeric_denom(val));
1642 }
1643 
1644 const char*
1645 printable_value (double val, int denom)
1646 {
1647  auto num = gnc_numeric_create(round(val * denom), denom);
1648  auto info = gnc_share_print_info_places(log10(denom));
1649  return xaccPrintAmount (num, info);
1650 }
1651 
1652 /********************************************************************\
1653  * xaccParseAmount *
1654  * parses amount strings using locale data *
1655  * *
1656  * Args: in_str -- pointer to string rep of num *
1657  * monetary -- boolean indicating whether value is monetary *
1658  * result -- pointer to result location, may be nullptr *
1659  * endstr -- used to store first digit not used in parsing *
1660  * Return: gboolean -- TRUE if a number found and parsed *
1661  * If FALSE, result is not changed *
1662 \********************************************************************/
1663 
1664 /* Parsing state machine states */
1665 enum ParseState
1666 {
1667  START_ST, /* Parsing initial whitespace */
1668  NEG_ST, /* Parsed a negative sign or a left paren */
1669  NUMER_ST, /* Parsing digits before grouping and decimal characters */
1670  FRAC_ST, /* Parsing the fractional portion of a number */
1671  DONE_ST, /* Finished, number is correct module grouping constraints */
1672  NO_NUM_ST /* Finished, number was malformed */
1673 };
1674 
1675 #define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST))
1676 
1677 static inline int64_t
1678 multiplier (int num_decimals)
1679 {
1680  switch (num_decimals)
1681  {
1682  case 12:
1683  return 1000000000000;
1684  case 11:
1685  return 100000000000;
1686  case 10:
1687  return 10000000000;
1688  case 9:
1689  return 1000000000;
1690  case 8:
1691  return 100000000;
1692  case 7:
1693  return 10000000;
1694  case 6:
1695  return 1000000;
1696  case 5:
1697  return 100000;
1698  case 4:
1699  return 10000;
1700  case 3:
1701  return 1000;
1702  case 2:
1703  return 100;
1704  case 1:
1705  return 10;
1706  case 0:
1707  return 1;
1708  default:
1709  PERR("bad fraction length");
1710  g_assert_not_reached();
1711  break;
1712  }
1713 
1714  return 1;
1715 }
1716 
1717 static gboolean
1718 xaccParseAmountInternal (const char* in_str, gboolean monetary,
1719  gunichar negative_sign, gunichar decimal_point,
1720  gunichar group_separator, const char* ignore_list,
1721  gboolean use_auto_decimal,
1722  gnc_numeric *result, char** endstr)
1723 {
1724  /* Initialize *endstr to in_str */
1725  if (endstr)
1726  *endstr = (char* ) in_str;
1727 
1728  if (!in_str)
1729  return FALSE;
1730 
1731  const char* in;
1732  if (!g_utf8_validate(in_str, -1, &in))
1733  {
1734  printf("Invalid utf8 string '%s'. Bad character at position %ld.\n",
1735  in_str, g_utf8_pointer_to_offset (in_str, in));
1736  return FALSE;
1737  }
1738 
1739  /* 'out_str' will be used to store digits for numeric conversion.
1740  * 'out' will be used to traverse out_str. */
1741  auto out_str = g_new(gchar, strlen(in_str) + 128);
1742  auto out = out_str;
1743 
1744  /* 'in' is used to traverse 'in_str'. */
1745  in = in_str;
1746 
1747  auto is_negative = false;
1748  auto got_decimal = false;
1749  auto need_paren = false;
1750  int64_t numer = 0;
1751  int64_t denom = 1;
1752 
1753  /* Initialize the state machine */
1754  auto state = START_ST;
1755 
1756  /* This while loop implements a state machine for parsing numbers. */
1757  while (TRUE)
1758  {
1759  auto next_state = state;
1760  auto uc = g_utf8_get_char(in);
1761 
1762  /* Ignore anything in the 'ignore list' */
1763  if (ignore_list && uc && g_utf8_strchr(ignore_list, -1, uc))
1764  {
1765  in = g_utf8_next_char(in);
1766  continue;
1767  }
1768 
1769  /* Note we never need to check for the end of 'in_str' explicitly.
1770  * The 'else' clauses on all the state transitions will handle that. */
1771  switch (state)
1772  {
1773  /* START_ST means we have parsed 0 or more whitespace characters */
1774  case START_ST:
1775  if (g_unichar_isdigit(uc))
1776  {
1777  int count = g_unichar_to_utf8(uc, out);
1778  out += count; /* we record the digits themselves in out_str
1779  * for later conversion by libc routines */
1780  next_state = NUMER_ST;
1781  }
1782  else if (uc == decimal_point)
1783  next_state = FRAC_ST;
1784  else if (g_unichar_isspace(uc))
1785  ;
1786  else if (uc == negative_sign)
1787  {
1788  is_negative = TRUE;
1789  next_state = NEG_ST;
1790  }
1791  else if (uc == '(')
1792  {
1793  is_negative = TRUE;
1794  need_paren = TRUE;
1795  next_state = NEG_ST;
1796  }
1797  else
1798  next_state = NO_NUM_ST;
1799 
1800  break;
1801 
1802  /* NEG_ST means we have just parsed a negative sign. For now,
1803  * we only recognize formats where the negative sign comes first. */
1804  case NEG_ST:
1805  if (g_unichar_isdigit(uc))
1806  {
1807  int count = g_unichar_to_utf8(uc, out);
1808  out += count;
1809  next_state = NUMER_ST;
1810  }
1811  else if (uc == decimal_point)
1812  next_state = FRAC_ST;
1813  else if (g_unichar_isspace(uc))
1814  ;
1815  else
1816  next_state = NO_NUM_ST;
1817 
1818  break;
1819 
1820  /* NUMER_ST means we have started parsing the number, but
1821  * have not encountered a decimal separator. */
1822  case NUMER_ST:
1823  if (g_unichar_isdigit(uc))
1824  {
1825  int count = g_unichar_to_utf8(uc, out);
1826  out += count;
1827  }
1828  else if (uc == decimal_point)
1829  next_state = FRAC_ST;
1830  else if (uc == group_separator)
1831  ; //ignore it
1832  else if (uc == ')' && need_paren)
1833  {
1834  next_state = DONE_ST;
1835  need_paren = FALSE;
1836  }
1837  else
1838  next_state = DONE_ST;
1839 
1840  break;
1841 
1842  /* FRAC_ST means we are now parsing fractional digits. */
1843  case FRAC_ST:
1844  if (g_unichar_isdigit(uc))
1845  {
1846  int count = g_unichar_to_utf8(uc, out);
1847  out += count;
1848  }
1849  else if (uc == decimal_point)
1850  {
1851  /* If a subsequent decimal point is also whitespace,
1852  * assume it was intended as such and stop parsing.
1853  * Otherwise, there is a problem. */
1854  if (g_unichar_isspace(decimal_point))
1855  next_state = DONE_ST;
1856  else
1857  next_state = NO_NUM_ST;
1858  }
1859  else if (uc == group_separator)
1860  {
1861  /* If a subsequent group separator is also whitespace,
1862  * assume it was intended as such and stop parsing.
1863  * Otherwise ignore it. */
1864  if (g_unichar_isspace(group_separator))
1865  next_state = DONE_ST;
1866  }
1867  else if (uc == ')' && need_paren)
1868  {
1869  next_state = DONE_ST;
1870  need_paren = FALSE;
1871  }
1872  else
1873  next_state = DONE_ST;
1874 
1875  break;
1876 
1877  default:
1878  PERR("bad state");
1879  g_assert_not_reached();
1880  break;
1881  }
1882 
1883  /* If we're moving into the FRAC_ST or out of the machine
1884  * without going through FRAC_ST, record the integral value. */
1885  if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
1886  ((next_state == DONE_ST) && !got_decimal))
1887  {
1888  *out = '\0';
1889 
1890  if (*out_str && sscanf(out_str, "%" SCNd64, &numer) < 1)
1891  next_state = NO_NUM_ST;
1892  else if (next_state == FRAC_ST)
1893  {
1894  /* reset the out pointer to record the fraction */
1895  out = out_str;
1896  *out = '\0';
1897 
1898  got_decimal = TRUE;
1899  }
1900  }
1901 
1902  state = next_state;
1903  if (done_state (state))
1904  break;
1905 
1906  in = g_utf8_next_char(in);
1907  }
1908 
1909  /* If there was an error, just quit */
1910  if (need_paren || (state == NO_NUM_ST))
1911  {
1912  g_free(out_str);
1913  return FALSE;
1914  }
1915 
1916  /* Cap the end of the fraction string, if any */
1917  *out = '\0';
1918 
1919  /* Add in fractional value */
1920  if (got_decimal && *out_str)
1921  {
1922 
1923  auto len = strlen(out_str);
1924 
1925  if (len > 12)
1926  {
1927  out_str[12] = '\0';
1928  len = 12;
1929  }
1930 
1931  int64_t fraction;
1932  if (sscanf (out_str, "%" SCNd64 , &fraction) < 1)
1933  {
1934  g_free(out_str);
1935  return FALSE;
1936  }
1937 
1938  denom = multiplier(len);
1939  numer *= denom;
1940  numer += fraction;
1941  }
1942  else if (monetary && use_auto_decimal && !got_decimal)
1943  {
1944  if ((auto_decimal_places > 0) && (auto_decimal_places <= 12))
1945  {
1946  denom = multiplier(auto_decimal_places);
1947 
1948  /* No need to multiply numer by denom at this point,
1949  * since by specifying the auto decimal places the
1950  * user has effectively determined the scaling factor
1951  * for the numerator they entered.
1952  */
1953  }
1954  }
1955 
1956  if (result)
1957  {
1958  *result = gnc_numeric_create (numer, denom);
1959  if (is_negative)
1960  *result = gnc_numeric_neg (*result);
1961  }
1962 
1963  if (endstr)
1964  *endstr = (char* ) in;
1965 
1966  g_free (out_str);
1967 
1968  return TRUE;
1969 }
1970 
1971 
1972 static gboolean
1973 xaccParseAmountBasicInternal (const char* in_str, gboolean monetary,
1974  gboolean use_auto_decimal, gnc_numeric *result,
1975  char** endstr, gboolean skip)
1976 {
1977  struct lconv *lc = gnc_localeconv();
1978  gunichar negative_sign = g_utf8_get_char(lc->negative_sign);
1979 
1980  gunichar decimal_point;
1981  gunichar group_separator;
1982  if (monetary)
1983  {
1984  group_separator = g_utf8_get_char(lc->mon_thousands_sep);
1985  decimal_point = g_utf8_get_char(lc->mon_decimal_point);
1986  }
1987  else
1988  {
1989  group_separator = g_utf8_get_char(lc->thousands_sep);
1990  decimal_point = g_utf8_get_char(lc->decimal_point);
1991  }
1992 
1993  const char* ignore = nullptr;
1994  if (skip)
1995  {
1996  /* We want the locale's positive sign to be ignored.
1997  * If the locale doesn't specify one, we assume "+" as
1998  * an optional positive sign and ignore that */
1999  ignore = lc->positive_sign;
2000  if (!ignore || !*ignore)
2001  ignore = "+";
2002  }
2003 
2004  return xaccParseAmountInternal(in_str, monetary, negative_sign,
2005  decimal_point, group_separator,
2006  ignore, use_auto_decimal,
2007  result, endstr);
2008 }
2009 
2010 
2011 gboolean
2012 xaccParseAmount (const char* in_str, gboolean monetary, gnc_numeric *result,
2013  char** endstr)
2014 {
2015  return xaccParseAmountBasicInternal (in_str, monetary, auto_decimal_enabled,
2016  result, endstr, FALSE);
2017 }
2018 
2019 gboolean
2020 xaccParseAmountImport (const char* in_str, gboolean monetary,
2021  gnc_numeric *result,
2022  char** endstr, gboolean skip)
2023 {
2024  return xaccParseAmountBasicInternal (in_str, monetary, FALSE,
2025  result, endstr, skip);
2026 }
2027 
2028 
2029 gboolean
2030 xaccParseAmountExtended (const char* in_str, gboolean monetary,
2031  gunichar negative_sign, gunichar decimal_point,
2032  gunichar group_separator, const char* ignore_list,
2033  gnc_numeric *result, char** endstr)
2034 {
2035  return xaccParseAmountInternal (in_str, monetary, negative_sign,
2036  decimal_point, group_separator,
2037  ignore_list, auto_decimal_enabled,
2038  result, endstr);
2039 }
2040 
2041 gboolean
2042 xaccParseAmountExtImport (const char* in_str, gboolean monetary,
2043  gunichar negative_sign, gunichar decimal_point,
2044  gunichar group_separator, const char* ignore_list,
2045  gnc_numeric *result, char** endstr)
2046 {
2047  return xaccParseAmountInternal (in_str, monetary, negative_sign,
2048  decimal_point, group_separator,
2049  ignore_list, FALSE,
2050  result, endstr);
2051 }
2052 
2053 
2054 /* enable/disable the auto_decimal_enabled option */
2055 static void
2056 gnc_set_auto_decimal_enabled (gpointer settings, char* key, gpointer user_data)
2057 {
2058  auto_decimal_enabled =
2059  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
2060 }
2061 
2062 /* set the number of auto decimal places to use */
2063 static void
2064 gnc_set_auto_decimal_places (gpointer settings, char* key, gpointer user_data)
2065 {
2066  auto_decimal_places =
2067  gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
2068 }
2069 
2070 static void
2071 gnc_auto_decimal_init (void)
2072 {
2073  auto_decimal_enabled =
2074  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
2075  auto_decimal_places =
2076  gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
2077 }
2078 
2079 void
2080 gnc_ui_util_init (void)
2081 {
2082  gnc_configure_account_separator ();
2083  gnc_auto_decimal_init();
2084 
2085  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR,
2086  (void*)gnc_configure_account_separator, nullptr);
2087  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE,
2088  (void*)gnc_configure_reverse_balance, nullptr);
2089  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT,
2090  (void*)gnc_configure_reverse_balance, nullptr);
2091  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP,
2092  (void*)gnc_configure_reverse_balance, nullptr);
2093  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_LOCALE,
2094  (void*)gnc_currency_changed_cb, nullptr);
2095  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_OTHER,
2096  (void*)gnc_currency_changed_cb, nullptr);
2097  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_OTHER,
2098  (void*)gnc_currency_changed_cb, nullptr);
2099  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_LOCALE,
2100  (void*)gnc_currency_changed_cb, nullptr);
2101  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_OTHER,
2102  (void*)gnc_currency_changed_cb, nullptr);
2103  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_OTHER,
2104  (void*)gnc_currency_changed_cb, nullptr);
2105  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT,
2106  (void*)gnc_set_auto_decimal_enabled, nullptr);
2107  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES,
2108  (void*)gnc_set_auto_decimal_places, nullptr);
2109 
2110 }
2111 
2112 void
2113 gnc_ui_util_remove_registered_prefs (void)
2114 {
2115  // remove the registered pref call backs above
2116  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2117  GNC_PREF_ACCOUNT_SEPARATOR,
2118  (void*)gnc_configure_account_separator, nullptr);
2119  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2120  GNC_PREF_REVERSED_ACCTS_NONE,
2121  (void*)gnc_configure_reverse_balance, nullptr);
2122  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2123  GNC_PREF_REVERSED_ACCTS_CREDIT,
2124  (void*)gnc_configure_reverse_balance, nullptr);
2125  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2126  GNC_PREF_REVERSED_ACCTS_INC_EXP,
2127  (void*)gnc_configure_reverse_balance, nullptr);
2128  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2129  GNC_PREF_CURRENCY_CHOICE_LOCALE,
2130  (void*)gnc_currency_changed_cb, nullptr);
2131  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2132  GNC_PREF_CURRENCY_CHOICE_OTHER,
2133  (void*)gnc_currency_changed_cb, nullptr);
2134  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2135  GNC_PREF_CURRENCY_OTHER,
2136  (void*)gnc_currency_changed_cb, nullptr);
2137  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2138  GNC_PREF_CURRENCY_CHOICE_LOCALE,
2139  (void*)gnc_currency_changed_cb, nullptr);
2140  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2141  GNC_PREF_CURRENCY_CHOICE_OTHER,
2142  (void*)gnc_currency_changed_cb, nullptr);
2143  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2144  GNC_PREF_CURRENCY_OTHER,
2145  (void*)gnc_currency_changed_cb, nullptr);
2146  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2147  GNC_PREF_AUTO_DECIMAL_POINT,
2148  (void*)gnc_set_auto_decimal_enabled, nullptr);
2149  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2150  GNC_PREF_AUTO_DECIMAL_PLACES,
2151  (void*)gnc_set_auto_decimal_places, nullptr);
2152 }
2153 
2154 static inline bool
2155 unichar_is_cntrl (gunichar uc)
2156 {
2157  return (uc < 0x20 || (uc > 0x7e && uc < 0xa0));
2158 }
2159 
2160 char*
2162 {
2163  bool cntrl = false;
2164  bool text_found = false;
2165 
2166  if (!text)
2167  return nullptr;
2168 
2169  if (!g_utf8_validate (text, -1, nullptr))
2170  return nullptr;
2171 
2172  auto filtered = g_string_sized_new (strlen (text) + 1);
2173 
2174  auto ch = text;
2175 
2176  while (*ch)
2177  {
2178  auto uc = g_utf8_get_char (ch);
2179 
2180  // check for starting with control characters
2181  if (unichar_is_cntrl (uc) && !text_found)
2182  {
2183  ch = g_utf8_next_char (ch);
2184  continue;
2185  }
2186  // check for alpha, num and punctuation
2187  if (!unichar_is_cntrl (uc))
2188  {
2189  filtered = g_string_append_unichar (filtered, uc);
2190  text_found = true;
2191  }
2192  // check for control characters after text
2193  if (unichar_is_cntrl (uc))
2194  cntrl = true;
2195 
2196  ch = g_utf8_next_char (ch);
2197 
2198  if (cntrl) // if control characters in text replace with space
2199  {
2200  auto uc2 = g_utf8_get_char (ch);
2201 
2202  if (!unichar_is_cntrl (uc2))
2203  filtered = g_string_append_unichar (filtered, ' ');
2204  }
2205  cntrl = false;
2206  }
2207  return g_string_free (filtered, FALSE);
2208 }
2209 
2210 void
2211 gnc_filter_text_set_cursor_position (const char* incoming_text,
2212  const char* symbol,
2213  gint *cursor_position)
2214 {
2215  int num = 0;
2216 
2217  if (*cursor_position == 0)
2218  return;
2219 
2220  if (!incoming_text || !symbol)
2221  return;
2222 
2223  if (g_strrstr (incoming_text, symbol) == nullptr)
2224  return;
2225 
2226  auto text_len = g_utf8_strlen (incoming_text, -1);
2227 
2228  for (int x = 0; x < text_len; x++)
2229  {
2230  auto temp = g_utf8_offset_to_pointer (incoming_text, x);
2231 
2232  if (g_str_has_prefix (temp, symbol))
2233  num++;
2234 
2235  if (g_strrstr (temp, symbol) == nullptr)
2236  break;
2237  }
2238  *cursor_position = *cursor_position - (num * g_utf8_strlen (symbol, -1));
2239 }
2240 
2241 char*
2242 gnc_filter_text_for_currency_symbol (const char* incoming_text,
2243  const char* symbol)
2244 {
2245  if (!incoming_text)
2246  return nullptr;
2247 
2248  if (!symbol)
2249  return g_strdup (incoming_text);
2250 
2251  if (g_strrstr (incoming_text, symbol) == nullptr)
2252  return g_strdup (incoming_text);
2253 
2254  auto split = g_strsplit (incoming_text, symbol, -1);
2255 
2256  auto ret_text = g_strjoinv (nullptr, split);
2257 
2258  g_strfreev (split);
2259  return ret_text;
2260 }
2261 
2262 char*
2263 gnc_filter_text_for_currency_commodity (const gnc_commodity *comm,
2264  const char* incoming_text,
2265  const char** symbol)
2266 {
2267  if (!incoming_text)
2268  {
2269  *symbol = nullptr;
2270  return nullptr;
2271  }
2272 
2273  if (!gnc_commodity_is_currency (comm))
2274  {
2275  *symbol = nullptr;
2276  return g_strdup (incoming_text);
2277  }
2278 
2279  if (comm)
2280  *symbol = gnc_commodity_get_nice_symbol (comm);
2281  else
2283 
2284  return gnc_filter_text_for_currency_symbol (incoming_text, *symbol);
2285 }
2286 
2287 gchar*
2288 gnc_list_formatter (GList *strings)
2289 {
2290  g_return_val_if_fail (strings, nullptr);
2291 
2292  UErrorCode status = U_ZERO_ERROR;
2293  auto formatter = icu::ListFormatter::createInstance(status);
2294  std::vector<UniStr> strvec;
2295  UniStr result;
2296  std::string retval;
2297 
2298  for (auto n = strings; n; n = g_list_next (n))
2299  {
2300  auto utf8_str{static_cast<const char*>(n->data)};
2301  strvec.push_back (UniStr::fromUTF8(utf8_str));
2302  }
2303 
2304  formatter->format (strvec.data(), strvec.size(), result, status);
2305 
2306  if (U_FAILURE(status))
2307  PERR ("Unicode error");
2308  else
2309  result.toUTF8String(retval);
2310 
2311  delete formatter;
2312  return g_strdup (retval.c_str());
2313 }
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account&#39;s type.
Definition: Account.cpp:2402
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:92
Never round at all, and signal an error if there is a fractional result in a computation.
Definition: gnc-numeric.h:177
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:381
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
gboolean xaccParseAmount(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr)
Parses in_str to obtain a numeric result.
gboolean xaccParseAmountImport(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr, gboolean skip)
Similar to xaccParseAmount, but with two differences.
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gnc_commodity * gnc_locale_default_currency_nodefault(void)
Returns the default currency of the current locale, or NULL if no sensible currency could be identifi...
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship, and will then add the account as a child of the new parent.
Definition: Account.cpp:2787
const char * gnc_print_amount_with_bidi_ltr_isolate(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
utility functions for the GnuCash UI
Expense accounts are used to denote expenses.
Definition: Account.h:143
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3217
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2696
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
STRUCTS.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.
char * gnc_wrap_text_with_bidi_ltr_isolate(const char *text)
This function helps with GTK&#39;s use of &#39;Unicode Bidirectional Text Algorithm&#39;.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
Attempt to convert the denominator to an exact power of ten without rounding.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
stop here; the following types just aren&#39;t ready for prime time
Definition: Account.h:161
char * gnc_filter_text_for_currency_commodity(const gnc_commodity *comm, const char *incoming_text, const char **symbol)
Returns the incoming text removed of currency symbol.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
Definition: gnc-numeric.h:188
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
Definition: gnc-prefs.c:319
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
gnc_commodity * gnc_default_report_currency(void)
Return the default currency for use in reports, as set by the user.
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
#define VREC
split is void
Definition: Split.h:77
gnc_numeric gnc_numeric_reduce(gnc_numeric in)
Return input after reducing it by Greater Common Factor (GCF) elimination.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Account * gnc_account_lookup_by_name(const Account *parent, const char *name)
The gnc_account_lookup_by_name() subroutine fetches the account by name from the descendants of the s...
Definition: Account.cpp:3043
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
gboolean xaccParseAmountExtImport(const char *in_str, gboolean monetary, gunichar negative_sign, gunichar decimal_point, gunichar group_separator, const char *ignore_list, gnc_numeric *result, char **endstr)
Similar to xaccParseAmountExtended, but will not automatically set a decimal point, regardless of what the user has set for this option.
char * gnc_get_account_name_for_register(const Account *account)
Get either the full name of the account or the simple name, depending on the configuration parameter ...
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account&#39;s commodity that the split should have...
Definition: gmock-Split.cpp:77
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3246
Account handling public routines.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Change the denominator of a gnc_numeric value to the specified denominator under standard arguments &#39;...
Income accounts are used to denote income.
Definition: Account.h:140
#define YREC
The Split has been reconciled.
Definition: Split.h:74
char * gnc_get_account_name_for_split_register(const Account *account, gboolean show_leaf_accounts)
Get either the full name of the account or the simple name, depending on the show_leaf_accounts.
const char * gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
Returns a string representation of the given GNCNumericErrorCode.
const char * gnc_get_doclink_flag_order(void)
Get a string containing document link flag order.
#define FREC
frozen into accounting period
Definition: Split.h:75
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)
Converts a string to a gnc_numeric.
void gnc_filter_text_set_cursor_position(const char *incoming_text, const char *symbol, int *zcursor_position)
Updates cursor_position after removal of currency symbols.
gchar * gnc_list_formatter(GList *strings)
This function takes a GList of char*, and uses locale-sensitive list formatter.
Account * gnc_account_lookup_by_full_name(const Account *any_acc, const gchar *name)
The gnc_account_lookup_full_name() subroutine works like gnc_account_lookup_by_name, but uses fully-qualified names using the given separator.
Definition: Account.cpp:3113
A/P account type.
Definition: Account.h:151
const char * gnc_commodity_get_nice_symbol(const gnc_commodity *cm)
Retrieve a symbol for the specified commodity, suitable for display to the user.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
void xaccAccountSetIsOpeningBalance(Account *acc, gboolean val)
Set the "opening-balance" flag for an account.
Definition: Account.cpp:4092
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition: Account.cpp:2927
All type declarations for the whole Gnucash engine.
#define CREC
The Split has been cleared.
Definition: Split.h:73
int xaccSPrintAmount(char *bufp, gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
gnc_commodity * gnc_account_get_currency_or_parent(const Account *account)
Returns a gnc_commodity that is a currency, suitable for being a Transaction&#39;s currency.
Definition: Account.cpp:3366
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
char * gnc_filter_text_for_control_chars(const char *text)
Returns the incoming text removed of control characters.
Account * gnc_account_lookup_for_register(const Account *base_account, const char *name)
Retrieve the account matching the given name starting from the descendants of base_account.
Generic api to store and retrieve preferences.
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
gnc_commodity * gnc_account_or_default_currency(const Account *account, gboolean *currency_from_account_found)
Returns a gnc_commodity that is a currency, suitable for being a Transaction&#39;s currency.
char * gnc_filter_text_for_currency_symbol(const char *incoming_text, const char *symbol)
Returns the incoming text removed of a currency symbol.
Truncate fractions (round toward zero)
Definition: gnc-numeric.h:152
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:119
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1475
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3359
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1048
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition: Account.cpp:1271
const char * gnc_get_doclink_str(char link_flag)
Get a string representing the document link type.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
const char * gnc_get_doclink_valid_flags(void)
Get a string containing documentation link valid flags.
Account * gnc_account_get_root(Account *acc)
This routine returns the root account of the account tree that the specified account belongs to...
Definition: Account.cpp:2893
Account * gnc_account_lookup_by_opening_balance(Account *account, gnc_commodity *commodity)
Find the opening balance account for the currency.
Definition: Account.cpp:3071
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3239
Equity account is used to balance the balance sheet.
Definition: Account.h:146
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1516
gnc_commodity * gnc_locale_default_currency(void)
Returns the default currency of the current locale.
void xaccAccountSetName(Account *acc, const char *str)
Set the account&#39;s name.
Definition: Account.cpp:2423
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
The Credit card account is used to denote credit (e.g.
Definition: Account.h:113
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account&#39;s commodity.
Definition: Account.cpp:2629
#define NREC
not reconciled or cleared
Definition: Split.h:76
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143
Utility functions for file access.