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