GnuCash  5.6-150-g038405b370+
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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  strcat (buf, temp_buf);
1294  }
1295 
1296  return strlen(buf);
1297 }
1298 
1302 int
1303 xaccSPrintAmount (char* bufp, gnc_numeric val, GNCPrintAmountInfo info)
1304 {
1305  auto orig_bufp = bufp;
1306  auto currency_symbol = "";
1307  const char* sign;
1308 
1309  char cs_precedes;
1310  char sep_by_space;
1311  char sign_posn;
1312 
1313  bool print_sign = true;
1314  bool print_absolute = false;
1315 
1316  if (!bufp)
1317  return 0;
1318 
1319  auto lc = gnc_localeconv();
1320  if (info.use_locale)
1321  if (gnc_numeric_negative_p (val))
1322  {
1323  cs_precedes = lc->n_cs_precedes;
1324  sep_by_space = lc->n_sep_by_space;
1325  }
1326  else
1327  {
1328  cs_precedes = lc->p_cs_precedes;
1329  sep_by_space = lc->p_sep_by_space;
1330  }
1331  else
1332  {
1333  cs_precedes = TRUE;
1334  sep_by_space = TRUE;
1335  }
1336 
1337  if (info.commodity && info.use_symbol)
1338  {
1339  currency_symbol = gnc_commodity_get_nice_symbol (info.commodity);
1340  if (!gnc_commodity_is_iso (info.commodity))
1341  {
1342  cs_precedes = FALSE;
1343  sep_by_space = TRUE;
1344  }
1345  }
1346 
1347  if (gnc_numeric_negative_p (val))
1348  {
1349  sign = lc->negative_sign;
1350  sign_posn = lc->n_sign_posn;
1351  }
1352  else
1353  {
1354  sign = lc->positive_sign;
1355  sign_posn = lc->p_sign_posn;
1356  }
1357 
1358  if (gnc_numeric_zero_p (val) || (sign == nullptr) || (sign[0] == 0))
1359  print_sign = FALSE;
1360 
1361  /* See if we print sign now */
1362  if (print_sign && (sign_posn == 1))
1363  bufp = g_stpcpy(bufp, sign);
1364 
1365  /* Now see if we print currency */
1366  if (cs_precedes)
1367  {
1368  /* See if we print sign now */
1369  if (print_sign && (sign_posn == 3))
1370  bufp = g_stpcpy(bufp, sign);
1371 
1372  if (info.use_symbol)
1373  {
1374  bufp = g_stpcpy(bufp, currency_symbol);
1375  if (sep_by_space)
1376  bufp = g_stpcpy(bufp, " ");
1377  }
1378 
1379  /* See if we print sign now */
1380  if (print_sign && (sign_posn == 4))
1381  bufp = g_stpcpy(bufp, sign);
1382  }
1383 
1384  /* Now see if we print parentheses */
1385  if (print_sign && (sign_posn == 0))
1386  {
1387  bufp = g_stpcpy(bufp, "(");
1388  print_absolute = TRUE;
1389  }
1390 
1391  /* Now print the value */
1392  bufp += PrintAmountInternal(bufp,
1393  print_absolute ? gnc_numeric_abs(val) : val,
1394  &info);
1395 
1396  /* Now see if we print parentheses */
1397  if (print_sign && (sign_posn == 0))
1398  bufp = g_stpcpy(bufp, ")");
1399 
1400  /* Now see if we print currency */
1401  if (!cs_precedes)
1402  {
1403  /* See if we print sign now */
1404  if (print_sign && (sign_posn == 3))
1405  bufp = g_stpcpy(bufp, sign);
1406 
1407  if (info.use_symbol)
1408  {
1409  if (sep_by_space)
1410  bufp = g_stpcpy(bufp, " ");
1411  bufp = g_stpcpy(bufp, currency_symbol);
1412  }
1413 
1414  /* See if we print sign now */
1415  if (print_sign && (sign_posn == 4))
1416  bufp = g_stpcpy(bufp, sign);
1417  }
1418 
1419  /* See if we print sign now */
1420  if (print_sign && (sign_posn == 2))
1421  bufp = g_stpcpy(bufp, sign);
1422 
1423  /* return length of printed string */
1424  return (bufp - orig_bufp);
1425 }
1426 
1427 #define BUFLEN 1024
1428 
1429 const char*
1430 xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info)
1431 {
1432  /* hack alert -- this is not thread safe ... */
1433  static char buf[BUFLEN];
1434 
1435  if (!xaccSPrintAmount (buf, val, info))
1436  buf[0] = '\0';
1437 
1438  /* its OK to return buf, since we declared it static */
1439  return buf;
1440 }
1441 
1442 const char*
1444 {
1445  /* hack alert -- this is not thread safe ... */
1446  static char buf[BUFLEN];
1447  static const char ltr_isolate[] = { '\xe2', '\x81', '\xa6' };
1448  static const char ltr_pop_isolate[] = { '\xe2', '\x81', '\xa9' };
1449  auto offset = info.use_symbol ? 3 : 0;
1450 
1451  if (!gnc_commodity_is_currency (info.commodity))
1452  offset = 0;
1453 
1454  memset (buf, 0, BUFLEN);
1455  if (!xaccSPrintAmount (buf + offset, val, info))
1456  {
1457  buf[0] = '\0';
1458  return buf;
1459  };
1460 
1461  if (offset == 0)
1462  return buf;
1463 
1464  memcpy (buf, ltr_isolate, 3);
1465 
1466  if (buf[BUFLEN - 4] == '\0')
1467  {
1468  auto length = strlen (buf);
1469  memcpy (buf + length, ltr_pop_isolate, 3);
1470  }
1471  else
1472  {
1473  buf[BUFLEN - 1] = '\0';
1474  memcpy (buf + BUFLEN - 4, ltr_pop_isolate, 3);
1475 
1476  PWARN("buffer length %d exceeded, string truncated was %s", BUFLEN, buf);
1477  }
1478  /* its OK to return buf, since we declared it static
1479  and is immediately g_strdup'd */
1480  return buf;
1481 }
1482 
1483 char*
1485 {
1486  static const char* ltr = "\u2066"; // ltr isolate
1487  static const char* pop = "\u2069"; // pop directional formatting
1488 
1489  if (!text)
1490  return nullptr;
1491 
1492  return g_strconcat (ltr, text, pop, nullptr);
1493 }
1494 
1495 /********************************************************************\
1496  ********************************************************************/
1497 
1498 #define FUDGE .00001
1499 
1500 /* This function is basically untranslatable. I'd
1501  guess out of the 29 translations we have, 20 will have their number
1502  wordings in a totally different way than English has (not to
1503  mention gender-dependent number endings). Which means this
1504  word-by-word translation will be useless or even plain
1505  wrong. For this reason, we don't even start to pretend a
1506  word-by-word translation would be of any use, so we don't mark any
1507  of these strings for translation. cstim, 2007-04-15. */
1508 static const char* small_numbers[] =
1509 {
1510  /* Translators: This section is for generating the "amount, in
1511  words" field when printing a check. This function gets the
1512  wording right for English, but unfortunately not for most other
1513  languages. Decide for yourself whether the check printing is
1514  actually needed in your language; if not, you can safely skip the
1515  translation of all of these strings. */
1516  "Zero", "One", "Two", "Three", "Four",
1517  "Five", "Six", "Seven", "Eight", "Nine",
1518  "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
1519  "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen",
1520  "Twenty"
1521 };
1522 static const char* medium_numbers[] =
1523 {
1524  "Zero", "Ten", "Twenty", "Thirty", "Forty",
1525  "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"
1526 };
1527 static const char* big_numbers[] =
1528 {
1529  /* Translators: This is the word for the number 10^2 */
1530  "Hundred",
1531  /* Translators: This is the word for the number 10^3 */
1532  "Thousand",
1533  /* Translators: This is the word for the number 10^6, one thousand
1534  thousands. */
1535  "Million",
1536  /* Translators: This is the word for the number 10^9, one thousand
1537  millions. WATCH OUT: In British English and many other languages
1538  this word is used for 10^12 which is one million millions! In
1539  contrast to this, here in GnuCash this is used in the American
1540  English meaning of 10^9. */
1541  "Billion",
1542  /* Translators: This is the word for the number 10^12, one million
1543  millions. */
1544  "Trillion",
1545  /* Translators: This is the word for the number 10^15 */
1546  "Quadrillion",
1547  /* Translators: This is the word for the number 10^18 */
1548  "Quintillion"
1549 };
1550 
1551 static char*
1552 integer_to_words(gint64 val)
1553 {
1554  if (val == 0)
1555  return g_strdup("zero");
1556  if (val < 0)
1557  val = -val;
1558 
1559  auto result = g_string_sized_new(100);
1560 
1561  while (val >= 1000)
1562  {
1563  int log_val = log10(val) / 3 + FUDGE;
1564  int pow_val = exp(log_val * 3 * G_LN10) + FUDGE;
1565  int this_part = val / pow_val;
1566  val -= this_part * pow_val;
1567  auto tmp = integer_to_words(this_part);
1568  g_string_append_printf(result, "%s %s ", tmp, gettext(big_numbers[log_val]));
1569  g_free(tmp);
1570  }
1571 
1572  if (val >= 100)
1573  {
1574  int this_part = val / 100;
1575  val -= this_part * 100;
1576  g_string_append_printf(result, "%s %s ",
1577  gettext(small_numbers[this_part]),
1578  gettext(big_numbers[0]));
1579  }
1580 
1581  if (val > 20)
1582  {
1583  int this_part = val / 10;
1584  val -= this_part * 10;
1585  g_string_append(result, gettext(medium_numbers[this_part]));
1586  g_string_append_c(result, ' ');
1587  }
1588 
1589  if (val > 0)
1590  {
1591  int this_part = val;
1592  g_string_append(result, gettext(small_numbers[this_part]));
1593  g_string_append_c(result, ' ');
1594  }
1595 
1596  result = g_string_truncate(result, result->len - 1);
1597  return g_string_free(result, FALSE);
1598 }
1599 
1600 #ifdef _MSC_VER
1601 static double round(double x)
1602 {
1603  // A simple round() implementation because MSVC doesn't seem to have that
1604  return floor(x + 0.5);
1605 }
1606 #endif
1607 
1608 char*
1609 number_to_words(double val, int64_t denom)
1610 {
1611  if (val < 0) val = -val;
1612  if (denom < 0) denom = -denom;
1613 
1614  auto int_part = floor(val);
1615  auto frac_part = static_cast<int64_t>(round((val - int_part) * denom));
1616 
1617  auto int_string = integer_to_words(int_part);
1618  /* Inside of the gettext macro _(...) we must not use any macros but
1619  only plain string literals. For this reason, convert the strings
1620  separately. */
1621  auto nomin_string = g_strdup_printf("%02" PRId64, frac_part);
1622  auto denom_string = g_strdup_printf("%" PRId64, denom);
1623  auto full_string =
1624  /* Translators: This is for the "amount, in words" field in check
1625  printing. The first %s is the integer amount of dollars (or
1626  whatever currency), the second and third %s the cent amount as
1627  a fraction, e.g. 47/100. */
1628  g_strdup_printf("%s and %s/%s",
1629  int_string, nomin_string, denom_string);
1630  g_free(int_string);
1631  g_free(nomin_string);
1632  g_free(denom_string);
1633  return full_string;
1634 }
1635 
1636 char*
1637 numeric_to_words(gnc_numeric val)
1638 {
1639  return number_to_words(gnc_numeric_to_double(val),
1640  gnc_numeric_denom(val));
1641 }
1642 
1643 const char*
1644 printable_value (double val, int denom)
1645 {
1646  auto num = gnc_numeric_create(round(val * denom), denom);
1647  auto info = gnc_share_print_info_places(log10(denom));
1648  return xaccPrintAmount (num, info);
1649 }
1650 
1651 /********************************************************************\
1652  * xaccParseAmount *
1653  * parses amount strings using locale data *
1654  * *
1655  * Args: in_str -- pointer to string rep of num *
1656  * monetary -- boolean indicating whether value is monetary *
1657  * result -- pointer to result location, may be nullptr *
1658  * endstr -- used to store first digit not used in parsing *
1659  * Return: gboolean -- TRUE if a number found and parsed *
1660  * If FALSE, result is not changed *
1661 \********************************************************************/
1662 
1663 /* Parsing state machine states */
1664 enum ParseState
1665 {
1666  START_ST, /* Parsing initial whitespace */
1667  NEG_ST, /* Parsed a negative sign or a left paren */
1668  NUMER_ST, /* Parsing digits before grouping and decimal characters */
1669  FRAC_ST, /* Parsing the fractional portion of a number */
1670  DONE_ST, /* Finished, number is correct module grouping constraints */
1671  NO_NUM_ST /* Finished, number was malformed */
1672 };
1673 
1674 #define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST))
1675 
1676 static inline int64_t
1677 multiplier (int num_decimals)
1678 {
1679  switch (num_decimals)
1680  {
1681  case 12:
1682  return 1000000000000;
1683  case 11:
1684  return 100000000000;
1685  case 10:
1686  return 10000000000;
1687  case 9:
1688  return 1000000000;
1689  case 8:
1690  return 100000000;
1691  case 7:
1692  return 10000000;
1693  case 6:
1694  return 1000000;
1695  case 5:
1696  return 100000;
1697  case 4:
1698  return 10000;
1699  case 3:
1700  return 1000;
1701  case 2:
1702  return 100;
1703  case 1:
1704  return 10;
1705  case 0:
1706  return 1;
1707  default:
1708  PERR("bad fraction length");
1709  g_assert_not_reached();
1710  break;
1711  }
1712 
1713  return 1;
1714 }
1715 
1716 static gboolean
1717 xaccParseAmountInternal (const char* in_str, gboolean monetary,
1718  gunichar negative_sign, gunichar decimal_point,
1719  gunichar group_separator, const char* ignore_list,
1720  gboolean use_auto_decimal,
1721  gnc_numeric *result, char** endstr)
1722 {
1723  /* Initialize *endstr to in_str */
1724  if (endstr)
1725  *endstr = (char* ) in_str;
1726 
1727  if (!in_str)
1728  return FALSE;
1729 
1730  const char* in;
1731  if (!g_utf8_validate(in_str, -1, &in))
1732  {
1733  printf("Invalid utf8 string '%s'. Bad character at position %ld.\n",
1734  in_str, g_utf8_pointer_to_offset (in_str, in));
1735  return FALSE;
1736  }
1737 
1738  /* 'out_str' will be used to store digits for numeric conversion.
1739  * 'out' will be used to traverse out_str. */
1740  auto out_str = g_new(gchar, strlen(in_str) + 128);
1741  auto out = out_str;
1742 
1743  /* 'in' is used to traverse 'in_str'. */
1744  in = in_str;
1745 
1746  auto is_negative = false;
1747  auto got_decimal = false;
1748  auto need_paren = false;
1749  int64_t numer = 0;
1750  int64_t denom = 1;
1751 
1752  /* Initialize the state machine */
1753  auto state = START_ST;
1754 
1755  /* This while loop implements a state machine for parsing numbers. */
1756  while (TRUE)
1757  {
1758  auto next_state = state;
1759  auto uc = g_utf8_get_char(in);
1760 
1761  /* Ignore anything in the 'ignore list' */
1762  if (ignore_list && uc && g_utf8_strchr(ignore_list, -1, uc))
1763  {
1764  in = g_utf8_next_char(in);
1765  continue;
1766  }
1767 
1768  /* Note we never need to check for the end of 'in_str' explicitly.
1769  * The 'else' clauses on all the state transitions will handle that. */
1770  switch (state)
1771  {
1772  /* START_ST means we have parsed 0 or more whitespace characters */
1773  case START_ST:
1774  if (g_unichar_isdigit(uc))
1775  {
1776  int count = g_unichar_to_utf8(uc, out);
1777  out += count; /* we record the digits themselves in out_str
1778  * for later conversion by libc routines */
1779  next_state = NUMER_ST;
1780  }
1781  else if (uc == decimal_point)
1782  next_state = FRAC_ST;
1783  else if (g_unichar_isspace(uc))
1784  ;
1785  else if (uc == negative_sign)
1786  {
1787  is_negative = TRUE;
1788  next_state = NEG_ST;
1789  }
1790  else if (uc == '(')
1791  {
1792  is_negative = TRUE;
1793  need_paren = TRUE;
1794  next_state = NEG_ST;
1795  }
1796  else
1797  next_state = NO_NUM_ST;
1798 
1799  break;
1800 
1801  /* NEG_ST means we have just parsed a negative sign. For now,
1802  * we only recognize formats where the negative sign comes first. */
1803  case NEG_ST:
1804  if (g_unichar_isdigit(uc))
1805  {
1806  int count = g_unichar_to_utf8(uc, out);
1807  out += count;
1808  next_state = NUMER_ST;
1809  }
1810  else if (uc == decimal_point)
1811  next_state = FRAC_ST;
1812  else if (g_unichar_isspace(uc))
1813  ;
1814  else
1815  next_state = NO_NUM_ST;
1816 
1817  break;
1818 
1819  /* NUMER_ST means we have started parsing the number, but
1820  * have not encountered a decimal separator. */
1821  case NUMER_ST:
1822  if (g_unichar_isdigit(uc))
1823  {
1824  int count = g_unichar_to_utf8(uc, out);
1825  out += count;
1826  }
1827  else if (uc == decimal_point)
1828  next_state = FRAC_ST;
1829  else if (uc == group_separator)
1830  ; //ignore it
1831  else if (uc == ')' && need_paren)
1832  {
1833  next_state = DONE_ST;
1834  need_paren = FALSE;
1835  }
1836  else
1837  next_state = DONE_ST;
1838 
1839  break;
1840 
1841  /* FRAC_ST means we are now parsing fractional digits. */
1842  case FRAC_ST:
1843  if (g_unichar_isdigit(uc))
1844  {
1845  int count = g_unichar_to_utf8(uc, out);
1846  out += count;
1847  }
1848  else if (uc == decimal_point)
1849  {
1850  /* If a subsequent decimal point is also whitespace,
1851  * assume it was intended as such and stop parsing.
1852  * Otherwise, there is a problem. */
1853  if (g_unichar_isspace(decimal_point))
1854  next_state = DONE_ST;
1855  else
1856  next_state = NO_NUM_ST;
1857  }
1858  else if (uc == group_separator)
1859  {
1860  /* If a subsequent group separator is also whitespace,
1861  * assume it was intended as such and stop parsing.
1862  * Otherwise ignore it. */
1863  if (g_unichar_isspace(group_separator))
1864  next_state = DONE_ST;
1865  }
1866  else if (uc == ')' && need_paren)
1867  {
1868  next_state = DONE_ST;
1869  need_paren = FALSE;
1870  }
1871  else
1872  next_state = DONE_ST;
1873 
1874  break;
1875 
1876  default:
1877  PERR("bad state");
1878  g_assert_not_reached();
1879  break;
1880  }
1881 
1882  /* If we're moving into the FRAC_ST or out of the machine
1883  * without going through FRAC_ST, record the integral value. */
1884  if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
1885  ((next_state == DONE_ST) && !got_decimal))
1886  {
1887  *out = '\0';
1888 
1889  if (*out_str && sscanf(out_str, "%" SCNd64, &numer) < 1)
1890  next_state = NO_NUM_ST;
1891  else if (next_state == FRAC_ST)
1892  {
1893  /* reset the out pointer to record the fraction */
1894  out = out_str;
1895  *out = '\0';
1896 
1897  got_decimal = TRUE;
1898  }
1899  }
1900 
1901  state = next_state;
1902  if (done_state (state))
1903  break;
1904 
1905  in = g_utf8_next_char(in);
1906  }
1907 
1908  /* If there was an error, just quit */
1909  if (need_paren || (state == NO_NUM_ST))
1910  {
1911  g_free(out_str);
1912  return FALSE;
1913  }
1914 
1915  /* Cap the end of the fraction string, if any */
1916  *out = '\0';
1917 
1918  /* Add in fractional value */
1919  if (got_decimal && *out_str)
1920  {
1921 
1922  auto len = strlen(out_str);
1923 
1924  if (len > 12)
1925  {
1926  out_str[12] = '\0';
1927  len = 12;
1928  }
1929 
1930  int64_t fraction;
1931  if (sscanf (out_str, "%" SCNd64 , &fraction) < 1)
1932  {
1933  g_free(out_str);
1934  return FALSE;
1935  }
1936 
1937  denom = multiplier(len);
1938  numer *= denom;
1939  numer += fraction;
1940  }
1941  else if (monetary && use_auto_decimal && !got_decimal)
1942  {
1943  if ((auto_decimal_places > 0) && (auto_decimal_places <= 12))
1944  {
1945  denom = multiplier(auto_decimal_places);
1946 
1947  /* No need to multiply numer by denom at this point,
1948  * since by specifying the auto decimal places the
1949  * user has effectively determined the scaling factor
1950  * for the numerator they entered.
1951  */
1952  }
1953  }
1954 
1955  if (result)
1956  {
1957  *result = gnc_numeric_create (numer, denom);
1958  if (is_negative)
1959  *result = gnc_numeric_neg (*result);
1960  }
1961 
1962  if (endstr)
1963  *endstr = (char* ) in;
1964 
1965  g_free (out_str);
1966 
1967  return TRUE;
1968 }
1969 
1970 
1971 static gboolean
1972 xaccParseAmountBasicInternal (const char* in_str, gboolean monetary,
1973  gboolean use_auto_decimal, gnc_numeric *result,
1974  char** endstr, gboolean skip)
1975 {
1976  struct lconv *lc = gnc_localeconv();
1977  gunichar negative_sign = g_utf8_get_char(lc->negative_sign);
1978 
1979  gunichar decimal_point;
1980  gunichar group_separator;
1981  if (monetary)
1982  {
1983  group_separator = g_utf8_get_char(lc->mon_thousands_sep);
1984  decimal_point = g_utf8_get_char(lc->mon_decimal_point);
1985  }
1986  else
1987  {
1988  group_separator = g_utf8_get_char(lc->thousands_sep);
1989  decimal_point = g_utf8_get_char(lc->decimal_point);
1990  }
1991 
1992  const char* ignore = nullptr;
1993  if (skip)
1994  {
1995  /* We want the locale's positive sign to be ignored.
1996  * If the locale doesn't specify one, we assume "+" as
1997  * an optional positive sign and ignore that */
1998  ignore = lc->positive_sign;
1999  if (!ignore || !*ignore)
2000  ignore = "+";
2001  }
2002 
2003  return xaccParseAmountInternal(in_str, monetary, negative_sign,
2004  decimal_point, group_separator,
2005  ignore, use_auto_decimal,
2006  result, endstr);
2007 }
2008 
2009 
2010 gboolean
2011 xaccParseAmount (const char* in_str, gboolean monetary, gnc_numeric *result,
2012  char** endstr)
2013 {
2014  return xaccParseAmountBasicInternal (in_str, monetary, auto_decimal_enabled,
2015  result, endstr, FALSE);
2016 }
2017 
2018 gboolean
2019 xaccParseAmountImport (const char* in_str, gboolean monetary,
2020  gnc_numeric *result,
2021  char** endstr, gboolean skip)
2022 {
2023  return xaccParseAmountBasicInternal (in_str, monetary, FALSE,
2024  result, endstr, skip);
2025 }
2026 
2027 
2028 gboolean
2029 xaccParseAmountExtended (const char* in_str, gboolean monetary,
2030  gunichar negative_sign, gunichar decimal_point,
2031  gunichar group_separator, const char* ignore_list,
2032  gnc_numeric *result, char** endstr)
2033 {
2034  return xaccParseAmountInternal (in_str, monetary, negative_sign,
2035  decimal_point, group_separator,
2036  ignore_list, auto_decimal_enabled,
2037  result, endstr);
2038 }
2039 
2040 gboolean
2041 xaccParseAmountExtImport (const char* in_str, gboolean monetary,
2042  gunichar negative_sign, gunichar decimal_point,
2043  gunichar group_separator, const char* ignore_list,
2044  gnc_numeric *result, char** endstr)
2045 {
2046  return xaccParseAmountInternal (in_str, monetary, negative_sign,
2047  decimal_point, group_separator,
2048  ignore_list, FALSE,
2049  result, endstr);
2050 }
2051 
2052 
2053 /* enable/disable the auto_decimal_enabled option */
2054 static void
2055 gnc_set_auto_decimal_enabled (gpointer settings, char* key, gpointer user_data)
2056 {
2057  auto_decimal_enabled =
2058  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
2059 }
2060 
2061 /* set the number of auto decimal places to use */
2062 static void
2063 gnc_set_auto_decimal_places (gpointer settings, char* key, gpointer user_data)
2064 {
2065  auto_decimal_places =
2066  gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
2067 }
2068 
2069 static void
2070 gnc_auto_decimal_init (void)
2071 {
2072  auto_decimal_enabled =
2073  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
2074  auto_decimal_places =
2075  gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
2076 }
2077 
2078 void
2079 gnc_ui_util_init (void)
2080 {
2081  gnc_configure_account_separator ();
2082  gnc_auto_decimal_init();
2083 
2084  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR,
2085  (void*)gnc_configure_account_separator, nullptr);
2086  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE,
2087  (void*)gnc_configure_reverse_balance, nullptr);
2088  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT,
2089  (void*)gnc_configure_reverse_balance, nullptr);
2090  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP,
2091  (void*)gnc_configure_reverse_balance, nullptr);
2092  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_LOCALE,
2093  (void*)gnc_currency_changed_cb, nullptr);
2094  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_OTHER,
2095  (void*)gnc_currency_changed_cb, nullptr);
2096  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_OTHER,
2097  (void*)gnc_currency_changed_cb, nullptr);
2098  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_LOCALE,
2099  (void*)gnc_currency_changed_cb, nullptr);
2100  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_OTHER,
2101  (void*)gnc_currency_changed_cb, nullptr);
2102  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_OTHER,
2103  (void*)gnc_currency_changed_cb, nullptr);
2104  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT,
2105  (void*)gnc_set_auto_decimal_enabled, nullptr);
2106  gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES,
2107  (void*)gnc_set_auto_decimal_places, nullptr);
2108 
2109 }
2110 
2111 void
2112 gnc_ui_util_remove_registered_prefs (void)
2113 {
2114  // remove the registered pref call backs above
2115  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2116  GNC_PREF_ACCOUNT_SEPARATOR,
2117  (void*)gnc_configure_account_separator, nullptr);
2118  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2119  GNC_PREF_REVERSED_ACCTS_NONE,
2120  (void*)gnc_configure_reverse_balance, nullptr);
2121  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2122  GNC_PREF_REVERSED_ACCTS_CREDIT,
2123  (void*)gnc_configure_reverse_balance, nullptr);
2124  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2125  GNC_PREF_REVERSED_ACCTS_INC_EXP,
2126  (void*)gnc_configure_reverse_balance, nullptr);
2127  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2128  GNC_PREF_CURRENCY_CHOICE_LOCALE,
2129  (void*)gnc_currency_changed_cb, nullptr);
2130  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2131  GNC_PREF_CURRENCY_CHOICE_OTHER,
2132  (void*)gnc_currency_changed_cb, nullptr);
2133  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2134  GNC_PREF_CURRENCY_OTHER,
2135  (void*)gnc_currency_changed_cb, nullptr);
2136  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2137  GNC_PREF_CURRENCY_CHOICE_LOCALE,
2138  (void*)gnc_currency_changed_cb, nullptr);
2139  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2140  GNC_PREF_CURRENCY_CHOICE_OTHER,
2141  (void*)gnc_currency_changed_cb, nullptr);
2142  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
2143  GNC_PREF_CURRENCY_OTHER,
2144  (void*)gnc_currency_changed_cb, nullptr);
2145  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2146  GNC_PREF_AUTO_DECIMAL_POINT,
2147  (void*)gnc_set_auto_decimal_enabled, nullptr);
2148  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2149  GNC_PREF_AUTO_DECIMAL_PLACES,
2150  (void*)gnc_set_auto_decimal_places, nullptr);
2151 }
2152 
2153 static inline bool
2154 unichar_is_cntrl (gunichar uc)
2155 {
2156  return (uc < 0x20 || (uc > 0x7e && uc < 0xa0));
2157 }
2158 
2159 char*
2161 {
2162  bool cntrl = false;
2163  bool text_found = false;
2164 
2165  if (!text)
2166  return nullptr;
2167 
2168  if (!g_utf8_validate (text, -1, nullptr))
2169  return nullptr;
2170 
2171  auto filtered = g_string_sized_new (strlen (text) + 1);
2172 
2173  auto ch = text;
2174 
2175  while (*ch)
2176  {
2177  auto uc = g_utf8_get_char (ch);
2178 
2179  // check for starting with control characters
2180  if (unichar_is_cntrl (uc) && !text_found)
2181  {
2182  ch = g_utf8_next_char (ch);
2183  continue;
2184  }
2185  // check for alpha, num and punctuation
2186  if (!unichar_is_cntrl (uc))
2187  {
2188  filtered = g_string_append_unichar (filtered, uc);
2189  text_found = true;
2190  }
2191  // check for control characters after text
2192  if (unichar_is_cntrl (uc))
2193  cntrl = true;
2194 
2195  ch = g_utf8_next_char (ch);
2196 
2197  if (cntrl) // if control characters in text replace with space
2198  {
2199  auto uc2 = g_utf8_get_char (ch);
2200 
2201  if (!unichar_is_cntrl (uc2))
2202  filtered = g_string_append_unichar (filtered, ' ');
2203  }
2204  cntrl = false;
2205  }
2206  return g_string_free (filtered, FALSE);
2207 }
2208 
2209 void
2210 gnc_filter_text_set_cursor_position (const char* incoming_text,
2211  const char* symbol,
2212  gint *cursor_position)
2213 {
2214  int num = 0;
2215 
2216  if (*cursor_position == 0)
2217  return;
2218 
2219  if (!incoming_text || !symbol)
2220  return;
2221 
2222  if (g_strrstr (incoming_text, symbol) == nullptr)
2223  return;
2224 
2225  auto text_len = g_utf8_strlen (incoming_text, -1);
2226 
2227  for (int x = 0; x < text_len; x++)
2228  {
2229  auto temp = g_utf8_offset_to_pointer (incoming_text, x);
2230 
2231  if (g_str_has_prefix (temp, symbol))
2232  num++;
2233 
2234  if (g_strrstr (temp, symbol) == nullptr)
2235  break;
2236  }
2237  *cursor_position = *cursor_position - (num * g_utf8_strlen (symbol, -1));
2238 }
2239 
2240 char*
2241 gnc_filter_text_for_currency_symbol (const char* incoming_text,
2242  const char* symbol)
2243 {
2244  if (!incoming_text)
2245  return nullptr;
2246 
2247  if (!symbol)
2248  return g_strdup (incoming_text);
2249 
2250  if (g_strrstr (incoming_text, symbol) == nullptr)
2251  return g_strdup (incoming_text);
2252 
2253  auto split = g_strsplit (incoming_text, symbol, -1);
2254 
2255  auto ret_text = g_strjoinv (nullptr, split);
2256 
2257  g_strfreev (split);
2258  return ret_text;
2259 }
2260 
2261 char*
2262 gnc_filter_text_for_currency_commodity (const gnc_commodity *comm,
2263  const char* incoming_text,
2264  const char** symbol)
2265 {
2266  if (!incoming_text)
2267  {
2268  *symbol = nullptr;
2269  return nullptr;
2270  }
2271 
2272  if (!gnc_commodity_is_currency (comm))
2273  {
2274  *symbol = nullptr;
2275  return g_strdup (incoming_text);
2276  }
2277 
2278  if (comm)
2279  *symbol = gnc_commodity_get_nice_symbol (comm);
2280  else
2282 
2283  return gnc_filter_text_for_currency_symbol (incoming_text, *symbol);
2284 }
2285 
2286 gchar*
2287 gnc_list_formatter (GList *strings)
2288 {
2289  g_return_val_if_fail (strings, nullptr);
2290 
2291  UErrorCode status = U_ZERO_ERROR;
2292  auto formatter = icu::ListFormatter::createInstance(status);
2293  std::vector<UniStr> strvec;
2294  UniStr result;
2295  std::string retval;
2296 
2297  for (auto n = strings; n; n = g_list_next (n))
2298  {
2299  auto utf8_str{static_cast<const char*>(n->data)};
2300  strvec.push_back (UniStr::fromUTF8(utf8_str));
2301  }
2302 
2303  formatter->format (strvec.data(), strvec.size(), result, status);
2304 
2305  if (U_FAILURE(status))
2306  PERR ("Unicode error");
2307  else
2308  result.toUTF8String(retval);
2309 
2310  delete formatter;
2311  return g_strdup (retval.c_str());
2312 }
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account&#39;s type.
Definition: Account.cpp:2422
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:2807
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:3237
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2716
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:320
#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 n)
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:3063
#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 ...
gdouble gnc_numeric_to_double(gnc_numeric n)
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:575
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:3275
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:3133
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:4108
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:2947
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:3378
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:1477
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:3371
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1052
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:1273
const char * gnc_get_doclink_str(char link_flag)
Get a string representing the document link type.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
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:2913
Account * gnc_account_lookup_by_opening_balance(Account *account, gnc_commodity *commodity)
Find the opening balance account for the currency.
Definition: Account.cpp:3091
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3259
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:1518
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:2443
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:2649
#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.