GnuCash  5.6-139-g03622b03d0+
gnc-commodity.cpp
1 /********************************************************************
2  * gnc-commodity.c -- api for tradable commodities (incl. currency) *
3  * Copyright (C) 2000 Bill Gribble *
4  * Copyright (C) 2001,2003 Linas Vepstas <linas@linas.org> *
5  * Copyright (c) 2006 David Hampton <hampton@employees.org> *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact: *
19  * *
20  * Free Software Foundation Voice: +1-617-542-5942 *
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22  * Boston, MA 02110-1301, USA gnu@gnu.org *
23  * *
24  *******************************************************************/
25 
26 #include <config.h>
27 
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <ctype.h>
31 #include <limits.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <regex.h>
36 #include <qofinstance-p.h>
37 
38 #include "gnc-commodity.hpp"
39 #include "gnc-commodity.h"
40 #include "gnc-locale-utils.h"
41 #include "gnc-prefs.h"
42 #include "guid.h"
43 #include "qofinstance.h"
44 
45 #include <list>
46 #include <unordered_map>
47 
48 static QofLogModule log_module = GNC_MOD_COMMODITY;
49 
50 /* Parts per unit is nominal, i.e. number of 'partname' units in
51  * a 'unitname' unit. fraction is transactional, i.e. how many
52  * of the smallest-transactional-units of the currency are there
53  * in a 'unitname' unit. */
54 
55 enum
56 {
57  PROP_0,
58  PROP_NAMESPACE, /* Table */
59  PROP_FULL_NAME, /* Table */
60  PROP_MNEMONIC, /* Table */
61  PROP_PRINTNAME, /* Constructed */
62  PROP_CUSIP, /* Table */
63  PROP_FRACTION, /* Table */
64  PROP_UNIQUE_NAME, /* Constructed */
65  PROP_QUOTE_FLAG, /* Table */
66  PROP_QUOTE_SOURCE, /* Table */
67  PROP_QUOTE_TZ, /* Table */
68 };
69 
71 {
72  QofInstance inst;
73 };
74 
75 typedef struct gnc_commodityPrivate
76 {
77  gnc_commodity_namespace *name_space;
78 
79  const char *fullname;
80  const char *mnemonic;
81  char *printname;
82  const char *cusip; /* CUSIP or other identifying code */
83  int fraction;
84  char *unique_name;
85  char *user_symbol;
86 
87  gboolean quote_flag; /* user wants price quotes */
88  gnc_quote_source *quote_source; /* current/old source of quotes */
89  const char *quote_tz;
90 
91  /* the number of accounts using this commodity - this field is not
92  * persisted */
93  int usage_count;
94 
95  /* the default display_symbol, set in iso-4217-currencies at start-up */
96  const char *default_symbol;
98 
99 #define GET_PRIVATE(o) \
100  ((gnc_commodityPrivate*)gnc_commodity_get_instance_private((gnc_commodity*)o))
101 
103 {
104  QofInstanceClass parent_class;
105 };
106 
107 static void commodity_free(gnc_commodity * cm);
108 static void gnc_commodity_set_default_symbol(gnc_commodity *, const char *);
109 
111 {
112  QofInstance inst;
113 
114  const gchar *name;
115  gboolean iso4217;
116  GHashTable * cm_table;
117  GList * cm_list;
118 };
119 
121 {
122  QofInstanceClass parent_class;
123 };
124 
126 {
127  GHashTable * ns_table;
128  GList * ns_list;
129 };
130 
131 static const std::unordered_map<std::string,std::string> gnc_new_iso_codes =
132 {
133  {"RUR", "RUB"}, /* Russian Ruble: RUR through 1997-12, RUB from 1998-01 onwards; see bug #393185 */
134  {"PLZ", "PLN"}, /* Polish Zloty */
135  {"UAG", "UAH"}, /* Ukraine Hryvnia */
136  {"NIS", "ILS"}, /* New Israeli Shekel: The informal abbreviation may be "NIS", but
137  its iso-4217 is clearly ILS and only this! Incorrectly changed
138  due to bug#152755 (Nov 2004) and changed back again by bug#492417
139  (Oct 2008). */
140  {"MXP", "MXN"}, /* Mexican (Nuevo) Peso */
141  {"TRL", "TRY"}, /* New Turkish Lira: changed 2005 */
142 
143  /* Only add currencies to this table when the old currency no longer
144  * exists in the file iso-4217-currencies.xml */
145 };
146 
147 static std::string fq_version;
148 
150 {
151 private:
152  gboolean m_supported;
153  QuoteSourceType m_type;
154  std::string m_user_name; /* User friendly name incl. region code*/
155  std::string m_internal_name; /* Name used internally and by finance::quote. */
156 public:
157  bool get_supported () const { return m_supported; }
158  void set_supported (bool supported) { m_supported = supported; }
159  QuoteSourceType get_type () const { return m_type; }
160  const char* get_user_name () const { return m_user_name.c_str(); }
161  const char* get_internal_name () const { return m_internal_name.c_str(); }
162  gnc_quote_source_s (gboolean supported, QuoteSourceType type,
163  const char* username, const char* int_name)
164  : m_supported{supported}
165  , m_type{type}
166  , m_user_name{username ? username : ""}
167  , m_internal_name{int_name ? int_name: ""} { };
168 };
169 
170 using QuoteSourceList = std::list<gnc_quote_source>;
171 
172 /* To update the following lists scan
173  * from github.com/finance-quote/finance-quote
174  * in lib/Finance/Quote/ all *.pm for "methods"
175  * because many of them have more than one -
176  * ideally after each release of them.
177  *
178  * Apply changes here also to the FQ appendix of help.
179  */
180 static QuoteSourceList currency_quote_sources =
181 {
182  { true, SOURCE_CURRENCY, "Currency", "currency" }
183 };
184 
185 /* The single quote method is usually the module name, but
186  * sometimes it gets the suffix "_direct"
187  * and the failover method is without suffix.
188  */
189 static QuoteSourceList single_quote_sources =
190 {
191  { false, SOURCE_SINGLE, "Alphavantage, US", "alphavantage" },
192  { false, SOURCE_SINGLE, "Amsterdam Euronext eXchange, NL", "aex" },
193  { false, SOURCE_SINGLE, "Association of Mutual Funds in India", "amfiindia" },
194  { false, SOURCE_SINGLE, "Australian Stock Exchange, AU", "asx" },
195  { false, SOURCE_SINGLE, "Canada Mutual", "canadamutual" },
196  { false, SOURCE_SINGLE, "Deka Investments, DE", "deka" },
197  { false, SOURCE_SINGLE, "Dutch", "dutch" },
198  { false, SOURCE_SINGLE, "DWS, DE", "dwsfunds" },
199  { false, SOURCE_SINGLE, "Financial Times Funds service, GB", "ftfunds" },
200  { false, SOURCE_SINGLE, "Finanzpartner, DE", "finanzpartner" },
201  { false, SOURCE_SINGLE, "GoldMoney spot rates, JE", "goldmoney" },
202  { false, SOURCE_SINGLE, "Google Web, US Stocks", "googleweb" },
203  { false, SOURCE_SINGLE, "India Mutual", "indiamutual" },
204  { false, SOURCE_SINGLE, "Morningstar, GB", "morningstaruk" },
205  { false, SOURCE_SINGLE, "Morningstar, JP", "morningstarjp" },
206  { false, SOURCE_SINGLE, "New Zealand stock eXchange, NZ", "nzx" },
207  { false, SOURCE_SINGLE, "Paris Stock Exchange/Boursorama, FR", "bourso" },
208  { false, SOURCE_SINGLE, "Romania", "romania" },
209  { false, SOURCE_SINGLE, "SIX Swiss Exchange shares, CH", "six" },
210  { false, SOURCE_SINGLE, "Skandinaviska Enskilda Banken, SE", "seb_funds" },
211  { false, SOURCE_SINGLE, "Sharenet, ZA", "za" },
212  { false, SOURCE_SINGLE, "TIAA-CREF, US", "tiaacref" },
213  { false, SOURCE_SINGLE, "Toronto Stock eXchange, CA", "tsx" },
214  { false, SOURCE_SINGLE, "T. Rowe Price", "troweprice" },
215  { false, SOURCE_SINGLE, "T. Rowe Price, US", "troweprice_direct" },
216  { false, SOURCE_SINGLE, "Union Investment, DE", "unionfunds" },
217  { false, SOURCE_SINGLE, "US Govt. Thrift Savings Plan", "tsp" },
218  { false, SOURCE_SINGLE, "Yahoo as JSON", "yahoo_json" },
219  { false, SOURCE_SINGLE, "Yahoo Web", "yahooweb" },
220 };
221 
222 static QuoteSourceList multiple_quote_sources =
223 {
224  { false, SOURCE_MULTI, "Australia (ASX, ...)", "australia" },
225  { false, SOURCE_MULTI, "Canada (Alphavantage, TSX, ...)", "canada" },
226  { false, SOURCE_MULTI, "Canada Mutual (Fund Library, StockHouse, ...)", "canadamutual" },
227  { false, SOURCE_MULTI, "Dutch (AEX, ...)", "dutch" },
228  { false, SOURCE_MULTI, "Europe (asegr,.bsero, hex ...)", "europe" },
229  { false, SOURCE_MULTI, "India Mutual (AMFI, ...)", "indiamutual" },
230  { false, SOURCE_MULTI, "France (bourso, ĺerevenu, ...)", "france" },
231  { false, SOURCE_MULTI, "Nasdaq (alphavantage, yahoo_json, ...)", "nasdaq" },
232  { false, SOURCE_MULTI, "NYSE (alphavantage, yahoo_json, ...)", "nyse" },
233  { false, SOURCE_MULTI, "South Africa (Sharenet, ...)", "za" },
234  { false, SOURCE_MULTI, "Romania (BSE-RO, ...)", "romania" },
235  { false, SOURCE_MULTI, "T. Rowe Price", "troweprice" },
236  { false, SOURCE_MULTI, "U.K. Funds (citywire, FTfunds, MStar, tnetuk, ...)", "ukfunds" },
237  { false, SOURCE_MULTI, "USA (alphavantage, yahoo_json, ...)", "usa" },
238 };
239 
240 static QuoteSourceList new_quote_sources;
241 
242 // cannot use map or unordered_map because order must be preserved
243 static const std::vector<std::pair<QuoteSourceType,QuoteSourceList&>> quote_sources_map =
244  {
245  { SOURCE_CURRENCY, currency_quote_sources },
246  { SOURCE_SINGLE, single_quote_sources },
247  { SOURCE_MULTI, multiple_quote_sources },
248  { SOURCE_UNKNOWN, new_quote_sources }
249  };
250 
251 /********************************************************************
252  * gnc_quote_source_fq_installed
253  *
254  * This function indicates whether or not the Finance::Quote module
255  * is installed on a users computer.
256  ********************************************************************/
257 gboolean
259 {
260  return (!fq_version.empty());
261 }
262 
263 
264 /********************************************************************
265  * gnc_quote_source_fq_version
266  *
267  * This function the version of the Finance::Quote module installed
268  * on a user's computer or nullptr if no installation is found.
269  ********************************************************************/
270 const char*
272 {
273  return fq_version.c_str();
274 }
275 
276 static QuoteSourceList&
277 get_quote_source_from_type (QuoteSourceType type)
278 {
279  auto quote_sources_it = std::find_if (quote_sources_map.begin(), quote_sources_map.end(),
280  [type] (const auto& qs) { return type == qs.first; });
281 
282  if (quote_sources_it != quote_sources_map.end())
283  return quote_sources_it->second;
284 
285  PWARN ("Invalid Quote Source %d, returning new_quote_sources", type);
286  return new_quote_sources;
287 }
288 
289 /********************************************************************
290  * gnc_quote_source_num_entries
291  *
292  * Return the number of entries for a given type of price source.
293  ********************************************************************/
295 {
296  auto source{get_quote_source_from_type(type)};
297  return std::distance(source.begin(), source.end());
298 }
299 
300 
301 
302 /********************************************************************
303  * gnc_quote_source_add_new
304  *
305  * Add a new price source. Called when unknown source names are found
306  * either in the F::Q installation (a newly available source) or in
307  * the user's data file (a source that has vanished but needs to be
308  * tracked.)
309  ********************************************************************/
310 gnc_quote_source *
311 gnc_quote_source_add_new (const char *source_name, gboolean supported)
312 {
313  DEBUG("Creating new source %s", (!source_name ? "(null)" : source_name));
314  /* This name can be changed if/when support for this price source is
315  * integrated into gnucash. */
316  /* This name is permanent and must be kept the same if/when support
317  * for this price source is integrated into gnucash (i.e. for a
318  * nice user name). */
319  return &new_quote_sources.emplace_back (supported, SOURCE_UNKNOWN, source_name, source_name);
320 }
321 
322 /********************************************************************
323  * gnc_quote_source_lookup_by_xxx
324  *
325  * Lookup a price source data structure based upon various criteria.
326  ********************************************************************/
327 gnc_quote_source *
329 {
330  ENTER("type/index is %d/%d", type, index);
331  auto& sources = get_quote_source_from_type (type);
332  if ((size_t) index < sources.size())
333  {
334  auto it = std::next(sources.begin(), index);
335  LEAVE("found %s", it->get_user_name());
336  return &*it;
337  }
338 
339  LEAVE("not found");
340  return nullptr;
341 }
342 
343 gnc_quote_source *
345 {
346  if (!name || !*name)
347  return nullptr;
348 
349  for (const auto& [_, sources] : quote_sources_map)
350  {
351  auto source_it = std::find_if (sources.begin(), sources.end(),
352  [name] (const auto& qs)
353  { return (g_strcmp0(name, qs.get_internal_name()) == 0); });
354  if (source_it != sources.end())
355  return &(*source_it);
356  }
357 
358  DEBUG("gnc_quote_source_lookup_by_internal: Unknown source %s", name);
359  return nullptr;
360 }
361 
362 /********************************************************************
363  * gnc_quote_source_get_xxx
364  *
365  * Accessor functions - get functions only. There are no set functions.
366  ********************************************************************/
368 gnc_quote_source_get_type (const gnc_quote_source *source)
369 {
370  ENTER("%p", source);
371  if (!source)
372  {
373  LEAVE("bad source");
374  return SOURCE_SINGLE;
375  }
376 
377  LEAVE("type is %d", source->get_type());
378  return source->get_type();
379 }
380 
381 gint
382 gnc_quote_source_get_index (const gnc_quote_source *source)
383 {
384  if (!source)
385  {
386  PWARN ("bad source");
387  return 0;
388  }
389 
390  auto& sources = get_quote_source_from_type (source->get_type());
391  auto is_source = [&source](const auto& findif_source)
392  { return &findif_source == source; };
393 
394  auto iter = std::find_if (sources.begin(), sources.end(), is_source);
395  if (iter != sources.end())
396  return std::distance (sources.begin(), iter);
397 
398  PWARN ("couldn't locate source");
399  return 0;
400 }
401 
402 gboolean
403 gnc_quote_source_get_supported (const gnc_quote_source *source)
404 {
405  ENTER("%p", source);
406  if (!source)
407  {
408  LEAVE("bad source");
409  return FALSE;
410  }
411 
412  LEAVE("%s supported", source && source->get_supported() ? "" : "not ");
413  return source->get_supported();
414 }
415 
416 const char *
417 gnc_quote_source_get_user_name (const gnc_quote_source *source)
418 {
419  ENTER("%p", source);
420  if (!source)
421  {
422  LEAVE("bad source");
423  return nullptr;
424  }
425  LEAVE("user name %s", source->get_user_name());
426  return source->get_user_name();
427 }
428 
429 const char *
430 gnc_quote_source_get_internal_name (const gnc_quote_source *source)
431 {
432  ENTER("%p", source);
433  if (!source)
434  {
435  LEAVE("bad source");
436  return nullptr;
437  }
438  LEAVE("internal name %s", source->get_internal_name());
439  return source->get_internal_name();
440 }
441 
442 
443 /********************************************************************
444  * gnc_quote_source_set_fq_installed
445  *
446  * Update gnucash internal tables on what Finance::Quote sources are
447  * installed.
448  ********************************************************************/
449 void
450 gnc_quote_source_set_fq_installed (const char* version_string,
451  const std::vector<std::string>& sources_list)
452 {
453  ENTER(" ");
454 
455  if (sources_list.empty())
456  return;
457 
458  if (version_string)
459  fq_version = version_string;
460  else
461  fq_version.clear();
462 
463  for (const auto& source_name_str : sources_list)
464  {
465  auto source_name = source_name_str.c_str();
466  auto source = gnc_quote_source_lookup_by_internal(source_name);
467 
468  if (source)
469  {
470  DEBUG("Found source %s: %s", source_name, source->get_user_name());
471  source->set_supported (true);
472  continue;
473  }
474 
475  gnc_quote_source_add_new(source_name, TRUE);
476  }
477  LEAVE(" ");
478 }
479 
480 /********************************************************************
481  * QoF Helpers
482  ********************************************************************/
483 
484 void
485 gnc_commodity_begin_edit (gnc_commodity *cm)
486 {
487  qof_begin_edit(&cm->inst);
488 }
489 
490 static void commit_err (QofInstance *inst, QofBackendError errcode)
491 {
492  PERR ("Failed to commit: %d", errcode);
493  gnc_engine_signal_commit_error( errcode );
494 }
495 
496 static void noop (QofInstance *inst) {}
497 
498 static void
499 comm_free(QofInstance* inst)
500 {
501  commodity_free( GNC_COMMODITY(inst) );
502 }
503 
504 void
505 gnc_commodity_commit_edit (gnc_commodity *cm)
506 {
507  if (!qof_commit_edit (QOF_INSTANCE(cm))) return;
508  qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free);
509 }
510 
511 /********************************************************************
512  * gnc_commodity_new
513  ********************************************************************/
514 
515 static void
516 mark_commodity_dirty (gnc_commodity *cm)
517 {
518  qof_instance_set_dirty(&cm->inst);
519  qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, nullptr);
520 }
521 
522 static void
523 reset_printname(gnc_commodityPrivate *priv)
524 {
525  g_free(priv->printname);
526  priv->printname = g_strdup_printf("%s (%s)",
527  priv->mnemonic ? priv->mnemonic : "",
528  priv->fullname ? priv->fullname : "");
529 }
530 
531 static void
532 reset_unique_name(gnc_commodityPrivate *priv)
533 {
534  gnc_commodity_namespace *ns;
535 
536  g_free(priv->unique_name);
537  ns = priv->name_space;
538  priv->unique_name = g_strdup_printf("%s::%s",
539  ns ? ns->name : "",
540  priv->mnemonic ? priv->mnemonic : "");
541 }
542 
543 /* GObject Initialization */
544 G_DEFINE_TYPE_WITH_PRIVATE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE)
545 
546 static void
547 gnc_commodity_init(gnc_commodity* com)
548 {
549  gnc_commodityPrivate* priv;
550 
551  priv = GET_PRIVATE(com);
552 
553  priv->name_space = nullptr;
554  priv->fullname = CACHE_INSERT("");
555  priv->mnemonic = CACHE_INSERT("");
556  priv->cusip = CACHE_INSERT("");
557  priv->fraction = 10000;
558  priv->quote_flag = 0;
559  priv->quote_source = nullptr;
560  priv->quote_tz = CACHE_INSERT("");
561 
562  reset_printname(priv);
563  reset_unique_name(priv);
564 }
565 
566 static void
567 gnc_commodity_dispose(GObject *comp)
568 {
569  G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp);
570 }
571 
572 static void
573 gnc_commodity_finalize(GObject* comp)
574 {
575  G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp);
576 }
577 /* Note that g_value_set_object() refs the object, as does
578  * g_object_get(). But g_object_get() only unrefs once when it disgorges
579  * the object, leaving an unbalanced ref, which leaks. So instead of
580  * using g_value_set_object(), use g_value_take_object() which doesn't
581  * ref the object when used in get_property().
582  */
583 static void
584 gnc_commodity_get_property (GObject *object,
585  guint prop_id,
586  GValue *value,
587  GParamSpec *pspec)
588 {
589  gnc_commodity *commodity;
590  gnc_commodityPrivate* priv;
591 
592  g_return_if_fail(GNC_IS_COMMODITY(object));
593 
594  commodity = GNC_COMMODITY(object);
595  priv = GET_PRIVATE(commodity);
596  switch (prop_id)
597  {
598  case PROP_NAMESPACE:
599  g_value_take_object(value, priv->name_space);
600  break;
601  case PROP_FULL_NAME:
602  g_value_set_string(value, priv->fullname);
603  break;
604  case PROP_MNEMONIC:
605  g_value_set_string(value, priv->mnemonic);
606  break;
607  case PROP_PRINTNAME:
608  g_value_set_string(value, priv->printname);
609  break;
610  case PROP_CUSIP:
611  g_value_set_string(value, priv->cusip);
612  break;
613  case PROP_FRACTION:
614  g_value_set_int(value, priv->fraction);
615  break;
616  case PROP_UNIQUE_NAME:
617  g_value_set_string(value, priv->unique_name);
618  break;
619  case PROP_QUOTE_FLAG:
620  g_value_set_boolean(value, priv->quote_flag);
621  break;
622  case PROP_QUOTE_SOURCE:
623  g_value_set_pointer(value, priv->quote_source);
624  break;
625  case PROP_QUOTE_TZ:
626  g_value_set_string(value, priv->quote_tz);
627  break;
628  default:
629  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
630  break;
631  }
632 }
633 
634 static void
635 gnc_commodity_set_property (GObject *object,
636  guint prop_id,
637  const GValue *value,
638  GParamSpec *pspec)
639 {
640  gnc_commodity *commodity;
641 
642  g_return_if_fail(GNC_IS_COMMODITY(object));
643 
644  commodity = GNC_COMMODITY(object);
645  g_assert (qof_instance_get_editlevel(commodity));
646 
647  switch (prop_id)
648  {
649  case PROP_NAMESPACE:
650  gnc_commodity_set_namespace(commodity, static_cast<const char*>(g_value_get_object(value)));
651  break;
652  case PROP_FULL_NAME:
653  gnc_commodity_set_fullname(commodity, g_value_get_string(value));
654  break;
655  case PROP_MNEMONIC:
656  gnc_commodity_set_mnemonic(commodity, g_value_get_string(value));
657  break;
658  case PROP_CUSIP:
659  gnc_commodity_set_cusip(commodity, g_value_get_string(value));
660  break;
661  case PROP_FRACTION:
662  gnc_commodity_set_fraction(commodity, g_value_get_int(value));
663  break;
664  case PROP_QUOTE_FLAG:
665  gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value));
666  break;
667  case PROP_QUOTE_SOURCE:
668  gnc_commodity_set_quote_source(commodity, static_cast<gnc_quote_source*>(g_value_get_pointer(value)));
669  break;
670  case PROP_QUOTE_TZ:
671  gnc_commodity_set_quote_tz(commodity, g_value_get_string(value));
672  break;
673  default:
674  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
675  break;
676  }
677 }
678 static void
679 gnc_commodity_class_init(struct _GncCommodityClass* klass)
680 {
681  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
682 
683  gobject_class->dispose = gnc_commodity_dispose;
684  gobject_class->finalize = gnc_commodity_finalize;
685  gobject_class->set_property = gnc_commodity_set_property;
686  gobject_class->get_property = gnc_commodity_get_property;
687 
688  g_object_class_install_property(gobject_class,
689  PROP_NAMESPACE,
690  g_param_spec_object ("namespace",
691  "Namespace",
692  "The namespace field denotes the "
693  "namespace for this commodity, either "
694  "a currency or symbol from a quote source.",
695  GNC_TYPE_COMMODITY_NAMESPACE,
696  G_PARAM_READWRITE));
697  g_object_class_install_property(gobject_class,
698  PROP_FULL_NAME,
699  g_param_spec_string ("fullname",
700  "Full Commodity Name",
701  "The fullname is the official full name of"
702  "the currency.",
703  nullptr,
704  G_PARAM_READWRITE));
705  g_object_class_install_property(gobject_class,
706  PROP_MNEMONIC,
707  g_param_spec_string ("mnemonic",
708  "Commodity Mnemonic",
709  "The mnemonic is the official abbreviated"
710  "designation for the currency.",
711  nullptr,
712  G_PARAM_READWRITE));
713  g_object_class_install_property(gobject_class,
714  PROP_PRINTNAME,
715  g_param_spec_string ("printname",
716  "Commodity Print Name",
717  "Printable form of the commodity name.",
718  nullptr,
719  G_PARAM_READABLE));
720  g_object_class_install_property(gobject_class,
721  PROP_CUSIP,
722  g_param_spec_string ("cusip",
723  "Commodity CUSIP Code",
724  "?????",
725  nullptr,
726  G_PARAM_READWRITE));
727  g_object_class_install_property(gobject_class,
728  PROP_FRACTION,
729  g_param_spec_int ("fraction",
730  "Fraction",
731  "The fraction is the number of sub-units that "
732  "the basic commodity can be divided into.",
733  1,
735  1,
736  G_PARAM_READWRITE));
737  g_object_class_install_property(gobject_class,
738  PROP_UNIQUE_NAME,
739  g_param_spec_string ("unique-name",
740  "Commodity Unique Name",
741  "Unique form of the commodity name which combines "
742  "the namespace name and the commodity name.",
743  nullptr,
744  G_PARAM_READABLE));
745  g_object_class_install_property(gobject_class,
746  PROP_QUOTE_FLAG,
747  g_param_spec_boolean ("quote_flag",
748  "Quote Flag",
749  "TRUE if prices are to be downloaded for this "
750  "commodity from a quote source.",
751  FALSE,
752  G_PARAM_READWRITE));
753  g_object_class_install_property(gobject_class,
754  PROP_QUOTE_SOURCE,
755  g_param_spec_pointer("quote-source",
756  "Quote Source",
757  "The quote source from which prices are downloaded.",
758  G_PARAM_READWRITE));
759  g_object_class_install_property(gobject_class,
760  PROP_QUOTE_TZ,
761  g_param_spec_string ("quote-tz",
762  "Commodity Quote Timezone",
763  "?????",
764  nullptr,
765  G_PARAM_READWRITE));
766 }
767 
768 gnc_commodity *
769 gnc_commodity_new(QofBook *book, const char * fullname,
770  const char * name_space, const char * mnemonic,
771  const char * cusip, int fraction)
772 {
773  auto retval = GNC_COMMODITY(g_object_new(GNC_TYPE_COMMODITY, nullptr));
774 
775  qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book);
776  gnc_commodity_begin_edit(retval);
777 
778  if ( name_space != nullptr )
779  {
780  /* Prevent setting anything except template in namespace template. */
781  if (g_strcmp0 (name_space, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
782  g_strcmp0 (mnemonic, "template") != 0)
783  {
784  PWARN("Converting commodity %s from namespace template to "
785  "namespace User", mnemonic);
786  name_space = "User";
787  }
788  gnc_commodity_set_namespace(retval, name_space);
789  if (gnc_commodity_namespace_is_iso(name_space))
790  {
793  }
794  }
795  gnc_commodity_set_fullname(retval, fullname);
796  gnc_commodity_set_mnemonic(retval, mnemonic);
797  gnc_commodity_set_cusip(retval, cusip);
798  gnc_commodity_set_fraction(retval, fraction);
799  mark_commodity_dirty (retval);
800  gnc_commodity_commit_edit(retval);
801 
802  qof_event_gen (&retval->inst, QOF_EVENT_CREATE, nullptr);
803 
804  return retval;
805 }
806 
807 
808 /********************************************************************
809  * gnc_commodity_destroy
810  ********************************************************************/
811 
812 static void
813 commodity_free(gnc_commodity * cm)
814 {
815  QofBook *book;
816  gnc_commodity_table *table;
817  gnc_commodityPrivate* priv;
818 
819  if (!cm) return;
820 
821  book = qof_instance_get_book(&cm->inst);
824  priv = GET_PRIVATE(cm);
825 
826  qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, nullptr);
827 
828  /* Set at creation */
829  CACHE_REMOVE (priv->fullname);
830  CACHE_REMOVE (priv->cusip);
831  CACHE_REMOVE (priv->mnemonic);
832  CACHE_REMOVE (priv->quote_tz);
833  priv->name_space = nullptr;
834 
835  /* Set through accessor functions */
836  priv->quote_source = nullptr;
837 
838  /* Automatically generated */
839  g_free(priv->printname);
840  priv->printname = nullptr;
841 
842  g_free(priv->unique_name);
843  priv->unique_name = nullptr;
844 
845 #ifdef ACCOUNTS_CLEANED_UP
846  /* Account objects are not actually cleaned up when a book is closed (in fact
847  * a memory leak), but commodities are, so in currently this warning gets hit
848  * quite frequently. Disable the check until cleaning up of accounts objects
849  * on close is implemented. */
850  if (priv->usage_count != 0)
851  {
852  PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm,
853  priv->usage_count);
854  }
855 #endif
856 
857  /* qof_instance_release (&cm->inst); */
858  g_object_unref(cm);
859 }
860 
861 void
862 gnc_commodity_destroy(gnc_commodity * cm)
863 {
864  gnc_commodity_begin_edit(cm);
865  qof_instance_set_destroying(cm, TRUE);
866  gnc_commodity_commit_edit(cm);
867 }
868 
869 void
870 gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src)
871 {
872  gnc_commodityPrivate* src_priv = GET_PRIVATE(src);
873  gnc_commodityPrivate* dest_priv = GET_PRIVATE(dest);
874 
875  gnc_commodity_set_fullname (dest, src_priv->fullname);
876  gnc_commodity_set_mnemonic (dest, src_priv->mnemonic);
877  dest_priv->name_space = src_priv->name_space;
878  gnc_commodity_set_fraction (dest, src_priv->fraction);
879  gnc_commodity_set_cusip (dest, src_priv->cusip);
880  gnc_commodity_set_quote_flag (dest, src_priv->quote_flag);
882  gnc_commodity_set_quote_tz (dest, src_priv->quote_tz);
883  qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
884 }
885 
886 gnc_commodity *
887 gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
888 {
889  gnc_commodityPrivate* src_priv;
890  gnc_commodityPrivate* dest_priv;
891 
892  auto dest = GNC_COMMODITY (g_object_new(GNC_TYPE_COMMODITY, nullptr));
893  qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book);
894  src_priv = GET_PRIVATE(src);
895  dest_priv = GET_PRIVATE(dest);
896 
897  dest_priv->fullname = CACHE_INSERT(src_priv->fullname);
898  dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic);
899  dest_priv->cusip = CACHE_INSERT(src_priv->cusip);
900  dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz);
901 
902  dest_priv->name_space = src_priv->name_space;
903 
904  dest_priv->fraction = src_priv->fraction;
905  dest_priv->quote_flag = src_priv->quote_flag;
906 
908 
909  qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
910 
911  reset_printname(dest_priv);
912  reset_unique_name(dest_priv);
913 
914  return dest;
915 }
916 
917 /********************************************************************
918  * gnc_commodity_get_mnemonic
919  ********************************************************************/
920 
921 const char *
922 gnc_commodity_get_mnemonic(const gnc_commodity * cm)
923 {
924  if (!cm) return nullptr;
925  return GET_PRIVATE(cm)->mnemonic;
926 }
927 
928 /********************************************************************
929  * gnc_commodity_get_printname
930  ********************************************************************/
931 
932 const char *
933 gnc_commodity_get_printname(const gnc_commodity * cm)
934 {
935  if (!cm) return nullptr;
936  return GET_PRIVATE(cm)->printname;
937 }
938 
939 
940 /********************************************************************
941  * gnc_commodity_get_namespace
942  ********************************************************************/
943 
944 const char *
945 gnc_commodity_get_namespace(const gnc_commodity * cm)
946 {
947  if (!cm) return nullptr;
948  return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
949 }
950 
951 gnc_commodity_namespace *
952 gnc_commodity_get_namespace_ds(const gnc_commodity * cm)
953 {
954  if (!cm) return nullptr;
955  return GET_PRIVATE(cm)->name_space;
956 }
957 
958 /********************************************************************
959  * gnc_commodity_get_fullname
960  ********************************************************************/
961 
962 const char *
963 gnc_commodity_get_fullname(const gnc_commodity * cm)
964 {
965  if (!cm) return nullptr;
966  return GET_PRIVATE(cm)->fullname;
967 }
968 
969 
970 /********************************************************************
971  * gnc_commodity_get_unique_name
972  ********************************************************************/
973 
974 const char *
975 gnc_commodity_get_unique_name(const gnc_commodity * cm)
976 {
977  if (!cm) return nullptr;
978  return GET_PRIVATE(cm)->unique_name;
979 }
980 
981 
982 /********************************************************************
983  * gnc_commodity_get_cusip
984  ********************************************************************/
985 
986 const char *
987 gnc_commodity_get_cusip(const gnc_commodity * cm)
988 {
989  if (!cm) return nullptr;
990  return GET_PRIVATE(cm)->cusip;
991 }
992 
993 /********************************************************************
994  * gnc_commodity_get_fraction
995  ********************************************************************/
996 
997 int
998 gnc_commodity_get_fraction(const gnc_commodity * cm)
999 {
1000  if (!cm) return 0;
1001  return GET_PRIVATE(cm)->fraction;
1002 }
1003 
1004 /********************************************************************
1005  * gnc_commodity_get_auto_quote_control_flag
1006  ********************************************************************/
1007 
1008 static gboolean
1009 gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
1010 {
1011  GValue v = G_VALUE_INIT;
1012  gboolean retval = TRUE;
1013 
1014  if (!cm) return FALSE;
1015  qof_instance_get_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
1016  if (G_VALUE_HOLDS_STRING (&v) &&
1017  strcmp(g_value_get_string (&v), "false") == 0)
1018  retval = FALSE;
1019  g_value_unset (&v);
1020  return retval;
1021 }
1022 
1023 /********************************************************************
1024  * gnc_commodity_get_quote_flag
1025  ********************************************************************/
1026 
1027 gboolean
1028 gnc_commodity_get_quote_flag(const gnc_commodity *cm)
1029 {
1030  if (!cm) return FALSE;
1031  return (GET_PRIVATE(cm)->quote_flag);
1032 }
1033 
1034 /********************************************************************
1035  * gnc_commodity_get_quote_source
1036  ********************************************************************/
1037 
1038 gnc_quote_source*
1039 gnc_commodity_get_quote_source(const gnc_commodity *cm)
1040 {
1041  gnc_commodityPrivate* priv;
1042 
1043  if (!cm) return nullptr;
1044  priv = GET_PRIVATE(cm);
1045  if (!priv->quote_source && gnc_commodity_is_iso(cm))
1046  return &currency_quote_sources.front();
1047  return priv->quote_source;
1048 }
1049 
1050 gnc_quote_source*
1051 gnc_commodity_get_default_quote_source(const gnc_commodity *cm)
1052 {
1053  if (cm && gnc_commodity_is_iso(cm))
1054  return &currency_quote_sources.front();
1055  /* Should make this a user option at some point. */
1056  return gnc_quote_source_lookup_by_internal("alphavantage");
1057 }
1058 
1059 /********************************************************************
1060  * gnc_commodity_get_quote_tz
1061  ********************************************************************/
1062 
1063 const char*
1064 gnc_commodity_get_quote_tz(const gnc_commodity *cm)
1065 {
1066  if (!cm) return nullptr;
1067  return GET_PRIVATE(cm)->quote_tz;
1068 }
1069 
1070 /********************************************************************
1071  * gnc_commodity_get_user_symbol
1072  ********************************************************************/
1073 const char*
1074 gnc_commodity_get_user_symbol(const gnc_commodity *cm)
1075 {
1076  g_return_val_if_fail (GNC_IS_COMMODITY (cm), nullptr);
1077 
1078  GValue v = G_VALUE_INIT;
1079  qof_instance_get_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
1080  const char *rv = G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : nullptr;
1081  g_value_unset (&v);
1082  return rv;
1083 }
1084 
1085 /********************************************************************
1086  * gnc_commodity_get_default_symbol
1087  *******************************************************************/
1088 const char*
1089 gnc_commodity_get_default_symbol(const gnc_commodity *cm)
1090 {
1091  if (!cm) return nullptr;
1092  return GET_PRIVATE(cm)->default_symbol;
1093 }
1094 
1095 /********************************************************************
1096  * gnc_commodity_get_nice_symbol
1097  *******************************************************************/
1098 const char*
1099 gnc_commodity_get_nice_symbol (const gnc_commodity *cm)
1100 {
1101  const char *nice_symbol;
1102  struct lconv *lc;
1103  if (!cm) return nullptr;
1104 
1105  nice_symbol = gnc_commodity_get_user_symbol(cm);
1106  if (nice_symbol && *nice_symbol)
1107  return nice_symbol;
1108 
1109  lc = gnc_localeconv();
1110  nice_symbol = lc->currency_symbol;
1111  if (!g_strcmp0(gnc_commodity_get_mnemonic(cm), lc->int_curr_symbol))
1112  return nice_symbol;
1113 
1114  nice_symbol = gnc_commodity_get_default_symbol(cm);
1115  if (nice_symbol && *nice_symbol)
1116  return nice_symbol;
1117 
1118  return gnc_commodity_get_mnemonic(cm);
1119 }
1120 
1121 /********************************************************************
1122  * gnc_commodity_set_mnemonic
1123  ********************************************************************/
1124 
1125 void
1126 gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic)
1127 {
1128  gnc_commodityPrivate* priv;
1129 
1130  if (!cm) return;
1131  priv = GET_PRIVATE(cm);
1132  if (priv->mnemonic == mnemonic) return;
1133 
1134  gnc_commodity_begin_edit(cm);
1135  CACHE_REMOVE (priv->mnemonic);
1136  priv->mnemonic = CACHE_INSERT(mnemonic);
1137 
1138  mark_commodity_dirty (cm);
1139  reset_printname(priv);
1140  reset_unique_name(priv);
1141  gnc_commodity_commit_edit(cm);
1142 }
1143 
1144 /********************************************************************
1145  * gnc_commodity_set_namespace
1146  ********************************************************************/
1147 
1148 void
1149 gnc_commodity_set_namespace(gnc_commodity * cm, const char * name_space)
1150 {
1151  QofBook *book;
1152  gnc_commodity_table *table;
1153  gnc_commodity_namespace *nsp;
1154  gnc_commodityPrivate* priv;
1155 
1156  if (!cm) return;
1157  priv = GET_PRIVATE(cm);
1158  book = qof_instance_get_book (&cm->inst);
1160  nsp = gnc_commodity_table_add_namespace(table, name_space, book);
1161  if (priv->name_space == nsp)
1162  return;
1163 
1164  gnc_commodity_begin_edit(cm);
1165  priv->name_space = nsp;
1166  if (nsp->iso4217)
1167  priv->quote_source = gnc_quote_source_lookup_by_internal("currency");
1168  mark_commodity_dirty(cm);
1169  reset_printname(priv);
1170  reset_unique_name(priv);
1171  gnc_commodity_commit_edit(cm);
1172 }
1173 
1174 /********************************************************************
1175  * gnc_commodity_set_fullname
1176  ********************************************************************/
1177 
1178 void
1179 gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname)
1180 {
1181  gnc_commodityPrivate* priv;
1182 
1183  if (!cm) return;
1184  priv = GET_PRIVATE(cm);
1185  if (priv->fullname == fullname) return;
1186 
1187  CACHE_REMOVE (priv->fullname);
1188  priv->fullname = CACHE_INSERT (fullname);
1189 
1190  gnc_commodity_begin_edit(cm);
1191  mark_commodity_dirty(cm);
1192  reset_printname(priv);
1193  gnc_commodity_commit_edit(cm);
1194 }
1195 
1196 /********************************************************************
1197  * gnc_commodity_set_cusip
1198  ********************************************************************/
1199 
1200 void
1201 gnc_commodity_set_cusip(gnc_commodity * cm,
1202  const char * cusip)
1203 {
1204  gnc_commodityPrivate* priv;
1205 
1206  if (!cm) return;
1207 
1208  priv = GET_PRIVATE(cm);
1209  if (priv->cusip == cusip) return;
1210 
1211  gnc_commodity_begin_edit(cm);
1212  CACHE_REMOVE (priv->cusip);
1213  priv->cusip = CACHE_INSERT (cusip);
1214  mark_commodity_dirty(cm);
1215  gnc_commodity_commit_edit(cm);
1216 }
1217 
1218 /********************************************************************
1219  * gnc_commodity_set_fraction
1220  ********************************************************************/
1221 
1222 void
1223 gnc_commodity_set_fraction(gnc_commodity * cm, int fraction)
1224 {
1225  if (!cm) return;
1226  gnc_commodity_begin_edit(cm);
1227  GET_PRIVATE(cm)->fraction = fraction;
1228  mark_commodity_dirty(cm);
1229  gnc_commodity_commit_edit(cm);
1230 }
1231 
1232 /********************************************************************
1233  * gnc_commodity_set_auto_quote_control_flag
1234  ********************************************************************/
1235 
1236 static void
1237 gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
1238  const gboolean flag)
1239 {
1240  GValue v = G_VALUE_INIT;
1241  ENTER ("(cm=%p, flag=%d)", cm, flag);
1242 
1243  if (!cm)
1244  {
1245  LEAVE("");
1246  return;
1247  }
1248  gnc_commodity_begin_edit(cm);
1249  if (flag)
1250  qof_instance_set_kvp (QOF_INSTANCE (cm), nullptr, 1, "auto_quote_control");
1251  else
1252  {
1253  g_value_init (&v, G_TYPE_STRING);
1254  g_value_set_string (&v, "false");
1255  qof_instance_set_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
1256  }
1257  g_value_unset (&v);
1258  mark_commodity_dirty(cm);
1259  gnc_commodity_commit_edit(cm);
1260  LEAVE("");
1261 }
1262 
1263 /********************************************************************
1264  * gnc_commodity_user_set_quote_flag
1265  ********************************************************************/
1266 
1267 void
1268 gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1269 {
1270  gnc_commodityPrivate* priv;
1271 
1272  ENTER ("(cm=%p, flag=%d)", cm, flag);
1273 
1274  if (!cm)
1275  {
1276  LEAVE("");
1277  return;
1278  }
1279 
1280  priv = GET_PRIVATE(cm);
1281  gnc_commodity_begin_edit(cm);
1282  gnc_commodity_set_quote_flag(cm, flag);
1283  if (gnc_commodity_is_iso(cm))
1284  {
1285  /* For currencies, disable auto quote control if the quote flag is being
1286  * changed from its default value and enable it if the quote flag is being
1287  * reset to its default value. The defaults for the quote flag are
1288  * disabled if no accounts are using the currency, and true otherwise.
1289  * Thus enable auto quote control if flag is FALSE and there are not any
1290  * accounts using this currency OR flag is TRUE and there are accounts
1291  * using this currency; otherwise disable auto quote control */
1292  gnc_commodity_set_auto_quote_control_flag(cm,
1293  (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0)));
1294  }
1295  gnc_commodity_commit_edit(cm);
1296  LEAVE("");
1297 }
1298 
1299 /********************************************************************
1300  * gnc_commodity_set_quote_flag
1301  ********************************************************************/
1302 
1303 void
1304 gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1305 {
1306  ENTER ("(cm=%p, flag=%d)", cm, flag);
1307 
1308  if (!cm) return;
1309  gnc_commodity_begin_edit(cm);
1310  GET_PRIVATE(cm)->quote_flag = flag;
1311  mark_commodity_dirty(cm);
1312  gnc_commodity_commit_edit(cm);
1313  LEAVE(" ");
1314 }
1315 
1316 /********************************************************************
1317  * gnc_commodity_set_quote_source
1318  ********************************************************************/
1319 
1320 void
1321 gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
1322 {
1323  ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->get_internal_name() : "unknown");
1324 
1325  if (!cm) return;
1326  gnc_commodity_begin_edit(cm);
1327  GET_PRIVATE(cm)->quote_source = src;
1328  mark_commodity_dirty(cm);
1329  gnc_commodity_commit_edit(cm);
1330  LEAVE(" ");
1331 }
1332 
1333 /********************************************************************
1334  * gnc_commodity_set_quote_tz
1335  ********************************************************************/
1336 
1337 void
1338 gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
1339 {
1340  gnc_commodityPrivate* priv;
1341 
1342  if (!cm) return;
1343 
1344  ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)");
1345 
1346  priv = GET_PRIVATE(cm);
1347 
1348  if (tz == priv->quote_tz)
1349  {
1350  LEAVE("Already correct TZ");
1351  return;
1352  }
1353 
1354  gnc_commodity_begin_edit(cm);
1355  CACHE_REMOVE (priv->quote_tz);
1356  priv->quote_tz = CACHE_INSERT (tz);
1357  mark_commodity_dirty(cm);
1358  gnc_commodity_commit_edit(cm);
1359  LEAVE(" ");
1360 }
1361 
1362 /********************************************************************
1363  * gnc_commodity_set_user_symbol
1364  ********************************************************************/
1365 
1366 void
1367 gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
1368 {
1369  struct lconv *lc;
1370 
1371  if (!cm) return;
1372 
1373  ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
1374 
1375  lc = gnc_localeconv();
1376  if (!user_symbol || !*user_symbol)
1377  user_symbol = nullptr;
1378  else if (!g_strcmp0(lc->int_curr_symbol, gnc_commodity_get_mnemonic(cm)) &&
1379  !g_strcmp0(lc->currency_symbol, user_symbol))
1380  /* if the user gives the ISO symbol for the locale currency or the
1381  * default symbol, actually remove the user symbol */
1382  user_symbol = nullptr;
1383  else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
1384  user_symbol = nullptr;
1385 
1386  gnc_commodity_begin_edit (cm);
1387 
1388  if (user_symbol)
1389  {
1390  GValue v = G_VALUE_INIT;
1391  g_value_init (&v, G_TYPE_STRING);
1392  g_value_set_static_string (&v, user_symbol);
1393  qof_instance_set_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
1394  g_value_unset (&v);
1395  }
1396  else
1397  {
1398  qof_instance_set_kvp (QOF_INSTANCE(cm), nullptr, 1, "user_symbol");
1399  }
1400 
1401  mark_commodity_dirty(cm);
1402  gnc_commodity_commit_edit(cm);
1403 
1404  LEAVE(" ");
1405 }
1406 
1407 /********************************************************************
1408  * gnc_commodity_set_default_symbol
1409  * Not made visible in gnc-commodity.h, it is only called from
1410  * iso-4217-currencies.c at startup.
1411  ********************************************************************/
1412 void
1413 gnc_commodity_set_default_symbol(gnc_commodity * cm,
1414  const char * default_symbol)
1415 {
1416  GET_PRIVATE(cm)->default_symbol = default_symbol;
1417 }
1418 
1419 /********************************************************************
1420  * gnc_commodity_increment_usage_count
1421  ********************************************************************/
1422 
1423 void
1425 {
1426  gnc_commodityPrivate* priv;
1427 
1428  ENTER("(cm=%p)", cm);
1429 
1430  if (!cm)
1431  {
1432  LEAVE("");
1433  return;
1434  }
1435 
1436  priv = GET_PRIVATE(cm);
1437 
1438  if ((priv->usage_count == 0) && !priv->quote_flag
1439  && gnc_commodity_get_auto_quote_control_flag(cm)
1440  && gnc_commodity_is_iso(cm))
1441  {
1442  /* compatibility hack - Gnucash 1.8 gets currency quotes when a
1443  non-default currency is assigned to an account. */
1444  gnc_commodity_begin_edit(cm);
1445  gnc_commodity_set_quote_flag(cm, TRUE);
1447  gnc_commodity_get_default_quote_source(cm));
1448  gnc_commodity_commit_edit(cm);
1449  }
1450  priv->usage_count++;
1451  LEAVE("(usage_count=%d)", priv->usage_count);
1452 }
1453 
1454 /********************************************************************
1455  * gnc_commodity_decrement_usage_count
1456  ********************************************************************/
1457 
1458 void
1460 {
1461  gnc_commodityPrivate* priv;
1462 
1463  ENTER("(cm=%p)", cm);
1464 
1465  if (!cm)
1466  {
1467  LEAVE("");
1468  return;
1469  }
1470 
1471  priv = GET_PRIVATE(cm);
1472 
1473  if (priv->usage_count == 0)
1474  {
1475  PWARN("usage_count already zero");
1476  LEAVE("");
1477  return;
1478  }
1479 
1480  priv->usage_count--;
1481  if ((priv->usage_count == 0) && priv->quote_flag
1482  && gnc_commodity_get_auto_quote_control_flag(cm)
1483  && gnc_commodity_is_iso(cm))
1484  {
1485  /* if this is a currency with auto quote control enabled and no more
1486  * accounts reference this currency, disable quote retrieval */
1487  gnc_commodity_set_quote_flag(cm, FALSE);
1488  }
1489  LEAVE("(usage_count=%d)", priv->usage_count);
1490 }
1491 
1492 /********************************************************************\
1493 \********************************************************************/
1494 
1495 
1496 /********************************************************************
1497  * gnc_commodity_equiv
1498  * are two commodities the same?
1499  ********************************************************************/
1500 
1501 gboolean
1502 gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b)
1503 {
1504  gnc_commodityPrivate* priv_a;
1505  gnc_commodityPrivate* priv_b;
1506 
1507  if (a == b) return TRUE;
1508  if (!a || !b) return FALSE;
1509 
1510  priv_a = GET_PRIVATE(a);
1511  priv_b = GET_PRIVATE(b);
1512  if (priv_a->name_space != priv_b->name_space) return FALSE;
1513  if (g_strcmp0(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE;
1514 
1515  return TRUE;
1516 }
1517 
1518 gboolean
1519 gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b)
1520 {
1521  return gnc_commodity_compare(a, b) == 0;
1522 }
1523 
1524 // Used as a sorting callback for deleting old prices, so it needs to be
1525 // stable but doesn't need to be in any particular order sensible to humans.
1526 int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b)
1527 {
1528  if (a == b) return 0;
1529  if (a && !b) return 1;
1530  if (b && !a) return -1;
1531  return qof_instance_guid_compare(a, b);
1532 }
1533 
1534 // Used as a callback to g_list_find_custom, it should return 0
1535 // when the commodities match.
1536 int gnc_commodity_compare_void(const void * a, const void * b)
1537 {
1538  return gnc_commodity_compare(GNC_COMMODITY (a), GNC_COMMODITY (b));
1539 }
1540 
1541 /************************************************************
1542  * Namespace functions *
1543  ************************************************************/
1544 const char *
1545 gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns)
1546 {
1547  if (ns == nullptr)
1548  return nullptr;
1549  return ns->name;
1550 }
1551 
1552 const char *
1553 gnc_commodity_namespace_get_gui_name (const gnc_commodity_namespace *ns)
1554 {
1555  if (ns == nullptr)
1556  return nullptr;
1557  if (g_strcmp0 (ns->name, GNC_COMMODITY_NS_CURRENCY) == 0)
1558  return GNC_COMMODITY_NS_ISO_GUI;
1559  return ns->name;
1560 }
1561 
1562 GList *
1563 gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
1564 {
1565  if (!name_space)
1566  return nullptr;
1567 
1568  return name_space->cm_list;
1569 }
1570 
1571 gboolean
1572 gnc_commodity_namespace_is_iso(const char *name_space)
1573 {
1574  return ((g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0) ||
1575  (g_strcmp0(name_space, GNC_COMMODITY_NS_CURRENCY) == 0));
1576 }
1577 
1578 static const gchar *
1579 gnc_commodity_table_map_namespace(const char * name_space)
1580 {
1581  if (g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0)
1582  return GNC_COMMODITY_NS_CURRENCY;
1583  return name_space;
1584 }
1585 
1586 /********************************************************************
1587  * gnc_commodity_table_new
1588  * make a new commodity table
1589  ********************************************************************/
1590 
1591 gnc_commodity_table *
1593 {
1594  gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1);
1595  retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal);
1596  retval->ns_list = nullptr;
1597  return retval;
1598 }
1599 
1600 /********************************************************************
1601  * book anchor functions
1602  ********************************************************************/
1603 
1604 gnc_commodity_table *
1606 {
1607  if (!book) return nullptr;
1608  return static_cast<gnc_commodity_table*>(qof_book_get_data (book, GNC_COMMODITY_TABLE));
1609 }
1610 
1611 gnc_commodity *
1612 gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book)
1613 {
1614  gnc_commodity *twin;
1615  const char * ucom;
1616  gnc_commodity_table * comtbl;
1617 
1618  if (!from) return nullptr;
1619  comtbl = gnc_commodity_table_get_table (book);
1620  if (!comtbl) return nullptr;
1621 
1622  ucom = gnc_commodity_get_unique_name (from);
1623  twin = gnc_commodity_table_lookup_unique (comtbl, ucom);
1624  if (!twin)
1625  {
1626  twin = gnc_commodity_clone (from, book);
1627  twin = gnc_commodity_table_insert (comtbl, twin);
1628  }
1629  return twin;
1630 }
1631 
1632 /********************************************************************
1633  * gnc_commodity_table_get_size
1634  * get the size of the commodity table
1635  ********************************************************************/
1636 
1637 static void
1638 count_coms(gpointer key, gpointer value, gpointer user_data)
1639 {
1640  GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table;
1641  guint *count = (guint*)user_data;
1642 
1643  if (g_strcmp0((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0)
1644  {
1645  /* don't count default commodities */
1646  return;
1647  }
1648 
1649  if (!value) return;
1650 
1651  *count += g_hash_table_size(tbl);
1652 }
1653 
1654 guint
1655 gnc_commodity_table_get_size(const gnc_commodity_table* tbl)
1656 {
1657  guint count = 0;
1658  g_return_val_if_fail(tbl, 0);
1659  g_return_val_if_fail(tbl->ns_table, 0);
1660 
1661  g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count);
1662 
1663  return count;
1664 }
1665 
1666 /********************************************************************
1667  * gnc_commodity_table_lookup
1668  * locate a commodity by namespace and mnemonic.
1669  ********************************************************************/
1670 
1671 gnc_commodity *
1672 gnc_commodity_table_lookup(const gnc_commodity_table * table,
1673  const char * name_space, const char * mnemonic)
1674 {
1675  gnc_commodity_namespace * nsp = nullptr;
1676 
1677  if (!table || !name_space || !mnemonic) return nullptr;
1678 
1679  nsp = gnc_commodity_table_find_namespace(table, name_space);
1680 
1681  if (nsp)
1682  {
1683  /*
1684  * Backward compatibility support for currencies that have
1685  * recently changed.
1686  */
1687  if (nsp->iso4217)
1688  {
1689  auto it = gnc_new_iso_codes.find (mnemonic);
1690  if (it != gnc_new_iso_codes.end())
1691  mnemonic = it->second.c_str();
1692  }
1693  return GNC_COMMODITY(g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic));
1694  }
1695  else
1696  {
1697  return nullptr;
1698  }
1699 }
1700 
1701 /********************************************************************
1702  * gnc_commodity_table_lookup
1703  * locate a commodity by unique name.
1704  ********************************************************************/
1705 
1706 gnc_commodity *
1707 gnc_commodity_table_lookup_unique(const gnc_commodity_table *table,
1708  const char * unique_name)
1709 {
1710  char *name_space;
1711  char *mnemonic;
1712  gnc_commodity *commodity;
1713 
1714  if (!table || !unique_name) return nullptr;
1715 
1716  name_space = g_strdup (unique_name);
1717  mnemonic = strstr (name_space, "::");
1718  if (!mnemonic)
1719  {
1720  g_free (name_space);
1721  return nullptr;
1722  }
1723 
1724  *mnemonic = '\0';
1725  mnemonic += 2;
1726 
1727  commodity = gnc_commodity_table_lookup (table, name_space, mnemonic);
1728 
1729  g_free (name_space);
1730 
1731  return commodity;
1732 }
1733 
1734 /********************************************************************
1735  * gnc_commodity_table_find_full
1736  * locate a commodity by namespace and printable name
1737  ********************************************************************/
1738 
1739 gnc_commodity *
1740 gnc_commodity_table_find_full(const gnc_commodity_table * table,
1741  const char * name_space,
1742  const char * fullname)
1743 {
1744  gnc_commodity * retval = nullptr;
1745  GList * all;
1746  GList * iterator;
1747 
1748  if (!fullname || (fullname[0] == '\0'))
1749  return nullptr;
1750 
1751  all = gnc_commodity_table_get_commodities(table, name_space);
1752 
1753  for (iterator = all; iterator; iterator = iterator->next)
1754  {
1755  auto commodity = GNC_COMMODITY (iterator->data);
1756  if (!strcmp(fullname,
1757  gnc_commodity_get_printname(commodity)))
1758  {
1759  retval = commodity;
1760  break;
1761  }
1762  }
1763 
1764  g_list_free (all);
1765 
1766  return retval;
1767 }
1768 
1769 
1770 /********************************************************************
1771  * gnc_commodity_table_insert
1772  * add a commodity to the table.
1773  ********************************************************************/
1774 
1775 gnc_commodity *
1776 gnc_commodity_table_insert(gnc_commodity_table * table,
1777  gnc_commodity * comm)
1778 {
1779  gnc_commodity_namespace * nsp = nullptr;
1780  gnc_commodity *c;
1781  const char *ns_name;
1782  gnc_commodityPrivate* priv;
1783  QofBook *book;
1784 
1785  if (!table) return nullptr;
1786  if (!comm) return nullptr;
1787 
1788  priv = GET_PRIVATE(comm);
1789 
1790  ENTER ("(table=%p, comm=%p) %s %s", table, comm,
1791  (priv->mnemonic == nullptr ? "(null)" : priv->mnemonic),
1792  (priv->fullname == nullptr ? "(null)" : priv->fullname));
1793  ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1794  c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1795 
1796  if (c)
1797  {
1798  if (c == comm)
1799  {
1800  LEAVE("already in table");
1801  return c;
1802  }
1803 
1804  /* Backward compatibility support for currencies that have
1805  * recently changed. */
1806  if (priv->name_space->iso4217)
1807  {
1808  auto it = gnc_new_iso_codes.find (priv->mnemonic);
1809  if (it != gnc_new_iso_codes.end())
1810  gnc_commodity_set_mnemonic(comm, it->second.c_str());
1811  }
1812  gnc_commodity_copy (c, comm);
1813  gnc_commodity_destroy (comm);
1814  LEAVE("found at %p", c);
1815  return c;
1816  }
1817 
1818  /* Prevent setting anything except template in namespace template. */
1819  if (g_strcmp0 (ns_name, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
1820  g_strcmp0 (priv->mnemonic, "template") != 0)
1821  {
1822  PWARN("Converting commodity %s from namespace template to "
1823  "namespace User", priv->mnemonic);
1824  gnc_commodity_set_namespace (comm, "User");
1825  ns_name = "User";
1826  mark_commodity_dirty (comm);
1827  }
1828 
1829  book = qof_instance_get_book (&comm->inst);
1830  nsp = gnc_commodity_table_add_namespace(table, ns_name, book);
1831 
1832  PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic,
1833  nsp->cm_table, nsp->name);
1834  g_hash_table_insert(nsp->cm_table,
1835  (gpointer)CACHE_INSERT(priv->mnemonic),
1836  (gpointer)comm);
1837  nsp->cm_list = g_list_append(nsp->cm_list, comm);
1838 
1839  qof_event_gen (&comm->inst, QOF_EVENT_ADD, nullptr);
1840  LEAVE ("(table=%p, comm=%p)", table, comm);
1841  return comm;
1842 }
1843 
1844 /********************************************************************
1845  * gnc_commodity_table_remove
1846  * remove a commodity from the table.
1847  ********************************************************************/
1848 
1849 void
1850 gnc_commodity_table_remove(gnc_commodity_table * table,
1851  gnc_commodity * comm)
1852 {
1853  gnc_commodity_namespace * nsp;
1854  gnc_commodity *c;
1855  gnc_commodityPrivate* priv;
1856  const char *ns_name;
1857 
1858  if (!table) return;
1859  if (!comm) return;
1860 
1861  priv = GET_PRIVATE(comm);
1862  ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1863  c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1864  if (c != comm) return;
1865 
1866  qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, nullptr);
1867 
1868  nsp = gnc_commodity_table_find_namespace(table, ns_name);
1869  if (!nsp) return;
1870 
1871  nsp->cm_list = g_list_remove(nsp->cm_list, comm);
1872  g_hash_table_remove (nsp->cm_table, priv->mnemonic);
1873  /* XXX minor mem leak, should remove the key as well */
1874 }
1875 
1876 /********************************************************************
1877  * gnc_commodity_table_has_namespace
1878  * see if the commodities namespace exists. May have zero commodities.
1879  ********************************************************************/
1880 
1881 int
1882 gnc_commodity_table_has_namespace(const gnc_commodity_table * table,
1883  const char * name_space)
1884 {
1885  gnc_commodity_namespace * nsp = nullptr;
1886 
1887  if (!table || !name_space)
1888  {
1889  return 0;
1890  }
1891 
1892  nsp = gnc_commodity_table_find_namespace(table, name_space);
1893  if (nsp)
1894  {
1895  return 1;
1896  }
1897  else
1898  {
1899  return 0;
1900  }
1901 }
1902 
1903 static void
1904 hash_keys_helper(gpointer key, gpointer value, gpointer data)
1905 {
1906  auto l = (GList**)data;
1907  *l = g_list_prepend(*l, key);
1908 }
1909 
1910 static GList *
1911 g_hash_table_keys(GHashTable * table)
1912 {
1913  GList * l = nullptr;
1914  g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l);
1915  return l;
1916 }
1917 
1918 static void
1919 hash_values_helper(gpointer key, gpointer value, gpointer data)
1920 {
1921  auto l = (GList**)data;
1922  *l = g_list_prepend(*l, value);
1923 }
1924 
1925 static GList *
1926 g_hash_table_values(GHashTable * table)
1927 {
1928  GList * l = nullptr;
1929  g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l);
1930  return l;
1931 }
1932 
1933 /********************************************************************
1934  * gnc_commodity_table_get_namespaces
1935  * see if any commodities in the namespace exist
1936  ********************************************************************/
1937 
1938 GList *
1939 gnc_commodity_table_get_namespaces(const gnc_commodity_table * table)
1940 {
1941  if (!table)
1942  return nullptr;
1943 
1944  return g_hash_table_keys(table->ns_table);
1945 }
1946 
1947 GList *
1949 {
1950  if (!table)
1951  return nullptr;
1952 
1953  return table->ns_list;
1954 }
1955 
1956 /* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to
1957  GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is
1958  either of these, the net result is that the iso4217 bit is set only
1959  for GNC_COMMODITY_NS_CURRENCY. This means that gnc_commodity_is_iso is
1960  a subset of gnc_commodity_is_currency. Most callers seem to use
1961  gnc_commodity_is_iso. */
1962 gboolean
1963 gnc_commodity_is_iso(const gnc_commodity * cm)
1964 {
1965  gnc_commodityPrivate* priv;
1966 
1967  if (!cm) return FALSE;
1968 
1969  priv = GET_PRIVATE(cm);
1970  if ( !priv->name_space) return FALSE;
1971  return priv->name_space->iso4217;
1972 }
1973 
1974 gboolean
1975 gnc_commodity_is_currency(const gnc_commodity *cm)
1976 {
1977  const char *ns_name;
1978  if (!cm) return FALSE;
1979 
1980  ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
1981  return (!g_strcmp0(ns_name, GNC_COMMODITY_NS_LEGACY) ||
1982  !g_strcmp0(ns_name, GNC_COMMODITY_NS_CURRENCY));
1983 }
1984 
1985 /********************************************************************
1986  * gnc_commodity_table_get_commodities
1987  * list commodities in a given namespace
1988  ********************************************************************/
1989 
1990 static CommodityList*
1991 commodity_table_get_all_noncurrency_commodities(const gnc_commodity_table* table)
1992 {
1993  GList *node = nullptr, *nslist = gnc_commodity_table_get_namespaces(table);
1994  CommodityList *retval = nullptr;
1995  for (node = nslist; node; node=g_list_next(node))
1996  {
1997  gnc_commodity_namespace *ns = nullptr;
1998  if (g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_CURRENCY) == 0
1999  || g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_TEMPLATE) == 0)
2000  continue;
2001  ns = gnc_commodity_table_find_namespace(table, (char*)(node->data));
2002  if (!ns)
2003  continue;
2004  retval = g_list_concat(g_hash_table_values(ns->cm_table), retval);
2005  }
2006  g_list_free(nslist);
2007  return retval;
2008 }
2009 
2010 CommodityList *
2011 gnc_commodity_table_get_commodities(const gnc_commodity_table * table,
2012  const char * name_space)
2013 {
2014  gnc_commodity_namespace * ns = nullptr;
2015 
2016  if (!table)
2017  return nullptr;
2018  if (g_strcmp0(name_space, GNC_COMMODITY_NS_NONISO_GUI) == 0)
2019  return commodity_table_get_all_noncurrency_commodities(table);
2020  ns = gnc_commodity_table_find_namespace(table, name_space);
2021  if (!ns)
2022  return nullptr;
2023 
2024  return g_hash_table_values(ns->cm_table);
2025 }
2026 
2027 /********************************************************************
2028  * gnc_commodity_table_get_quotable_commodities
2029  * list commodities in a given namespace that get price quotes
2030  ********************************************************************/
2031 
2032 static void
2033 get_quotables_helper1(gpointer key, gpointer value, gpointer data)
2034 {
2035  auto comm = GNC_COMMODITY(value);
2036  gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2037  auto l = static_cast<GList**>(data);
2038 
2039  if (!priv->quote_flag || !priv->quote_source || !priv->quote_source->get_supported())
2040  return;
2041  *l = g_list_prepend(*l, value);
2042 }
2043 
2044 static gboolean
2045 get_quotables_helper2 (gnc_commodity *comm, gpointer data)
2046 {
2047  auto l = static_cast<GList**>(data);
2048  gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2049 
2050  if (!priv->quote_flag || priv->quote_source || !priv->quote_source->get_supported())
2051  return TRUE;
2052  *l = g_list_prepend(*l, comm);
2053  return TRUE;
2054 }
2055 
2056 CommodityList *
2058 {
2059  gnc_commodity_namespace * ns = nullptr;
2060  const char *name_space;
2061  GList * nslist, * tmp;
2062  GList * l = nullptr;
2063  regex_t pattern;
2064  const char *expression = gnc_prefs_get_namespace_regexp();
2065 
2066  ENTER("table=%p, expression=%s", table, expression);
2067  if (!table)
2068  return nullptr;
2069 
2070  if (expression && *expression)
2071  {
2072  if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
2073  {
2074  LEAVE("Cannot compile regex");
2075  return nullptr;
2076  }
2077 
2079  for (tmp = nslist; tmp; tmp = tmp->next)
2080  {
2081  name_space = static_cast<const char*>(tmp->data);
2082  if (regexec(&pattern, name_space, 0, nullptr, 0) == 0)
2083  {
2084  DEBUG("Running list of %s commodities", name_space);
2085  ns = gnc_commodity_table_find_namespace(table, name_space);
2086  if (ns)
2087  {
2088  g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l);
2089  }
2090  }
2091  }
2092  g_list_free(nslist);
2093  regfree(&pattern);
2094  }
2095  else
2096  {
2097  gnc_commodity_table_foreach_commodity(table, get_quotables_helper2,
2098  (gpointer) &l);
2099  }
2100  LEAVE("list head %p", l);
2101  return l;
2102 }
2103 
2104 /********************************************************************
2105  * gnc_commodity_table_add_namespace
2106  * add an empty namespace if it does not exist
2107  ********************************************************************/
2108 
2109 /* GObject Initialization */
2110 QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE)
2111 
2112 static void
2113 gnc_commodity_namespace_init(gnc_commodity_namespace* ns)
2114 {
2115 }
2116 
2117 static void
2118 gnc_commodity_namespace_dispose_real (GObject *nsp)
2119 {
2120 }
2121 
2122 static void
2123 gnc_commodity_namespace_finalize_real(GObject* nsp)
2124 {
2125 }
2126 
2127 gnc_commodity_namespace *
2129  const char * name_space,
2130  QofBook *book)
2131 {
2132  gnc_commodity_namespace * ns = nullptr;
2133 
2134  if (!table) return nullptr;
2135 
2136  name_space = gnc_commodity_table_map_namespace(name_space);
2137  ns = gnc_commodity_table_find_namespace(table, name_space);
2138  if (!ns)
2139  {
2140  ns = static_cast<gnc_commodity_namespace*>(g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, nullptr));
2141  ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal);
2142  ns->name = CACHE_INSERT(static_cast<const char*>(name_space));
2143  ns->iso4217 = gnc_commodity_namespace_is_iso(name_space);
2144  qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book);
2145  qof_event_gen (&ns->inst, QOF_EVENT_CREATE, nullptr);
2146 
2147  g_hash_table_insert(table->ns_table,
2148  (gpointer) ns->name,
2149  (gpointer) ns);
2150  table->ns_list = g_list_append(table->ns_list, ns);
2151  qof_event_gen (&ns->inst, QOF_EVENT_ADD, nullptr);
2152  }
2153  return ns;
2154 }
2155 
2156 
2157 gnc_commodity_namespace *
2158 gnc_commodity_table_find_namespace(const gnc_commodity_table * table,
2159  const char * name_space)
2160 {
2161  if (!table || !name_space)
2162  return nullptr;
2163 
2164  name_space = gnc_commodity_table_map_namespace(name_space);
2165  return static_cast<gnc_commodity_namespace*>(g_hash_table_lookup(table->ns_table, (gpointer)name_space));
2166 }
2167 
2168 
2169 gnc_commodity *
2170 gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book)
2171 {
2172  QofCollection *col;
2173  if (!guid || !book) return nullptr;
2174  col = qof_book_get_collection (book, GNC_ID_COMMODITY);
2175  return (gnc_commodity *) qof_collection_lookup_entity (col, guid);
2176 }
2177 
2178 /********************************************************************
2179  * gnc_commodity_table_delete_namespace
2180  * delete a namespace
2181  ********************************************************************/
2182 
2183 static int
2184 ns_helper(gpointer key, gpointer value, gpointer user_data)
2185 {
2186  auto c = GNC_COMMODITY(value);
2188  CACHE_REMOVE(static_cast<char*>(key)); /* key is commodity mnemonic */
2189  return TRUE;
2190 }
2191 
2192 void
2194  const char * name_space)
2195 {
2196  gnc_commodity_namespace * ns;
2197 
2198  if (!table) return;
2199 
2200  ns = gnc_commodity_table_find_namespace(table, name_space);
2201  if (!ns)
2202  return;
2203 
2204  qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, nullptr);
2205  g_hash_table_remove(table->ns_table, name_space);
2206  table->ns_list = g_list_remove(table->ns_list, ns);
2207 
2208  g_list_free(ns->cm_list);
2209  ns->cm_list = nullptr;
2210 
2211  g_hash_table_foreach_remove(ns->cm_table, ns_helper, nullptr);
2212  g_hash_table_destroy(ns->cm_table);
2213  CACHE_REMOVE(ns->name);
2214 
2215  qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, nullptr);
2216  /* qof_instance_release(&ns->inst); */
2217  g_object_unref(ns);
2218 }
2219 
2220 /********************************************************************
2221  * gnc_commodity_table_foreach_commodity
2222  * call user-defined function once for every commodity in every
2223  * namespace
2224  ********************************************************************/
2225 
2226 typedef struct
2227 {
2228  gboolean ok;
2229  gboolean (*func)(gnc_commodity *, gpointer);
2230  gpointer user_data;
2231 } IterData;
2232 
2233 static void
2234 iter_commodity (gpointer key, gpointer value, gpointer user_data)
2235 {
2236  IterData *iter_data = (IterData *) user_data;
2237  gnc_commodity *cm = (gnc_commodity *) value;
2238 
2239  if (iter_data->ok)
2240  {
2241  iter_data->ok = (iter_data->func)(cm, iter_data->user_data);
2242  }
2243 }
2244 
2245 static void
2246 iter_namespace (gpointer key, gpointer value, gpointer user_data)
2247 {
2248  GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table;
2249  g_hash_table_foreach (namespace_hash, iter_commodity, user_data);
2250 }
2251 
2252 gboolean
2253 gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl,
2254  gboolean (*f)(gnc_commodity *, gpointer),
2255  gpointer user_data)
2256 {
2257  IterData iter_data;
2258 
2259  if (!tbl || !f) return FALSE;
2260 
2261  iter_data.ok = TRUE;
2262  iter_data.func = f;
2263  iter_data.user_data = user_data;
2264 
2265  g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data);
2266 
2267  return iter_data.ok;
2268 }
2269 
2270 /********************************************************************
2271  * gnc_commodity_table_destroy
2272  * cleanup and free.
2273  ********************************************************************/
2274 
2275 void
2276 gnc_commodity_table_destroy(gnc_commodity_table * t)
2277 {
2278  gnc_commodity_namespace * ns;
2279  GList *item, *next;
2280 
2281  if (!t) return;
2282  ENTER ("table=%p", t);
2283 
2284  for (item = t->ns_list; item; item = next)
2285  {
2286  next = g_list_next(item);
2287  ns = static_cast<gnc_commodity_namespace*>(item->data);
2289  }
2290 
2291  g_list_free(t->ns_list);
2292  t->ns_list = nullptr;
2293  g_hash_table_destroy(t->ns_table);
2294  t->ns_table = nullptr;
2295  LEAVE ("table=%p", t);
2296  g_free(t);
2297 }
2298 
2299 /* =========================================================== */
2300 
2301 /********************************************************************
2302  * gnc_commodity_table_add_default_data
2303  ********************************************************************/
2304 
2305 #define CUR_I18N(String) dgettext ("iso_4217", String)
2306 
2307 gboolean
2308 gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
2309 {
2310  QofCollection *col;
2311  gnc_commodity* c;
2312 
2313  ENTER ("table=%p", table);
2314  gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_TEMPLATE, book);
2315  c = gnc_commodity_new(book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1);
2317 
2318 #include "iso-4217-currencies.c"
2319 
2320  /* We've just created the default namespaces and currencies. Mark
2321  * these collections as clean because there is no USER entered data
2322  * in these collections as of yet. */
2323  col = qof_book_get_collection(book, GNC_ID_COMMODITY);
2325  col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE);
2327 
2328  LEAVE ("table=%p", table);
2329  return TRUE;
2330 }
2331 
2332 /********************************************************************
2333  ********************************************************************/
2334 /* QofObject function implementation and registration */
2335 
2336 #ifdef _MSC_VER
2337 /* MSVC compiler doesn't have C99 "designated initializers"
2338  * so we wrap them in a macro that is empty on MSVC. */
2339 # define DI(x) /* */
2340 #else
2341 # define DI(x) x
2342 #endif
2343 static QofObject commodity_object_def =
2344 {
2345  DI(.interface_version = ) QOF_OBJECT_VERSION,
2346  DI(.e_type = ) GNC_ID_COMMODITY,
2347  DI(.type_label = ) "Commodity",
2348  DI(.create = ) nullptr,
2349  DI(.book_begin = ) nullptr,
2350  DI(.book_end = ) nullptr,
2351  DI(.is_dirty = ) qof_collection_is_dirty,
2352  DI(.mark_clean = ) qof_collection_mark_clean,
2353  DI(.foreach = ) qof_collection_foreach,
2354  DI(.printable = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname,
2355 };
2356 
2357 static QofObject namespace_object_def =
2358 {
2359  DI(.interface_version = ) QOF_OBJECT_VERSION,
2360  DI(.e_type = ) GNC_ID_COMMODITY_NAMESPACE,
2361  DI(.type_label = ) "Namespace",
2362  DI(.create = ) nullptr,
2363  DI(.book_begin = ) nullptr,
2364  DI(.book_end = ) nullptr,
2365  DI(.is_dirty = ) nullptr,
2366  DI(.mark_clean = ) nullptr,
2367  DI(.foreach = ) nullptr,
2368  DI(.printable = ) nullptr,
2369 };
2370 
2371 static void
2372 commodity_table_book_begin (QofBook *book)
2373 {
2374  gnc_commodity_table *ct;
2375  ENTER ("book=%p", book);
2376 
2378  return;
2379 
2380  ct = gnc_commodity_table_new ();
2381  qof_book_set_data (book, GNC_COMMODITY_TABLE, ct);
2382 
2383  if (!gnc_commodity_table_add_default_data(ct, book))
2384  {
2385  PWARN("unable to initialize book's commodity_table");
2386  }
2387 
2388  LEAVE ("book=%p", book);
2389 }
2390 
2391 static void
2392 commodity_table_book_end (QofBook *book)
2393 {
2394  gnc_commodity_table *ct;
2395 
2396  ct = gnc_commodity_table_get_table (book);
2397  qof_book_set_data (book, GNC_COMMODITY_TABLE, nullptr);
2398  gnc_commodity_table_destroy (ct);
2399 }
2400 
2401 static QofObject commodity_table_object_def =
2402 {
2403  DI(.interface_version = ) QOF_OBJECT_VERSION,
2404  DI(.e_type = ) GNC_ID_COMMODITY_TABLE,
2405  DI(.type_label = ) "CommodityTable",
2406  DI(.create = ) nullptr,
2407  DI(.book_begin = ) commodity_table_book_begin,
2408  DI(.book_end = ) commodity_table_book_end,
2409  DI(.is_dirty = ) qof_collection_is_dirty,
2410  DI(.mark_clean = ) qof_collection_mark_clean,
2411  DI(.foreach = ) nullptr,
2412  DI(.printable = ) nullptr,
2413  DI(.version_cmp = ) nullptr,
2414 };
2415 
2416 gboolean
2418 {
2419  if (!qof_object_register (&commodity_object_def))
2420  return FALSE;
2421  if (!qof_object_register (&namespace_object_def))
2422  return FALSE;
2423  return qof_object_register (&commodity_table_object_def);
2424 }
2425 
2426 /* *******************************************************************
2427 * gnc_monetary methods
2428 ********************************************************************/
2429 
2431 MonetaryList *
2432 gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
2433 {
2434  MonetaryList *l = list, *tmp;
2435  for (tmp = list; tmp; tmp = tmp->next)
2436  {
2437  auto list_mon = static_cast<gnc_monetary*>(tmp->data);
2438  if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity))
2439  {
2440  list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
2442  break;
2443  }
2444  }
2445 
2446  /* See if we found an entry, and add one if not */
2447  if (tmp == nullptr)
2448  {
2449  auto new_mon = static_cast<gnc_monetary*>(g_new0(gnc_monetary, 1));
2450  *new_mon = add_mon;
2451  l = g_list_prepend(l, new_mon);
2452  }
2453 
2454  return l;
2455 }
2456 
2459 MonetaryList *
2461 {
2462  MonetaryList *node, *next;
2463  for (node = list; node; node = next)
2464  {
2465  auto mon = static_cast<gnc_monetary*>(node->data);
2466  next = node->next;
2467  if (gnc_numeric_zero_p(mon->value))
2468  {
2469  g_free(mon);
2470  list = g_list_delete_link(list, node);
2471  }
2472  }
2473  return list;
2474 }
2475 
2477 void
2478 gnc_monetary_list_free(MonetaryList *list)
2479 {
2480  MonetaryList *tmp;
2481  for (tmp = list; tmp; tmp = tmp->next)
2482  {
2483  g_free(tmp->data);
2484  }
2485 
2486  g_list_free(list);
2487 }
2488 
2489 /* ========================= END OF FILE ============================== */
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
const char * gnc_commodity_get_cusip(const gnc_commodity *cm)
Retrieve the &#39;exchange code&#39; for the specified commodity.
gboolean gnc_commodity_table_foreach_commodity(const gnc_commodity_table *table, gboolean(*f)(gnc_commodity *cm, gpointer user_data), gpointer user_data)
Call a function once for each commodity in the commodity table.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
A gnc_commodity_table is a database of commodity info.
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.
gboolean gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
Add all the standard namespaces and currencies to the commodity table.
const char * gnc_quote_source_get_user_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the user friendly name of this quote source...
void qof_instance_set_kvp(QofInstance *, GValue const *value, unsigned count,...)
Sets a KVP slot to a value from a GValue.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
#define GNC_COMMODITY_MAX_FRACTION
Max fraction is 10^9 because 10^10 would require changing it to an int64_t.
This quote source pulls from a single specific web site.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of &#39;dirty&#39; flag on collection.
Definition: qofid.cpp:255
gnc_quote_source * gnc_quote_source_add_new(const char *source_name, gboolean supported)
Create a new quote source.
const char * gnc_commodity_namespace_get_gui_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure in a form suitable to present to the user...
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition: qofid.cpp:212
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm)
Retrieve the automatic price quote flag for the specified commodity.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
const char * gnc_commodity_get_user_symbol(const gnc_commodity *cm)
Retrieve the user-defined symbol for the specified commodity.
void gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
Set the automatic price quote timezone for the specified commodity.
void gnc_commodity_decrement_usage_count(gnc_commodity *cm)
Decrement a commodity&#39;s internal counter that tracks how many accounts are using that commodity...
Commodity handling public routines (C++ api)
const char * gnc_commodity_get_quote_tz(const gnc_commodity *cm)
Retrieve the automatic price quote timezone for the specified commodity.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_commodity_set_fraction(gnc_commodity *cm, int fraction)
Set the fraction for the specified commodity.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
gboolean gnc_quote_source_get_supported(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the flag that indicates whether this particular quote...
globally unique ID User API
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
QuoteSourceType gnc_quote_source_get_type(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the type of this particular quote source...
The special currency quote source.
int gnc_commodity_compare_void(const void *a, const void *b)
A wrapper around gnc_commodity_compare() which offers the function declaration that is needed for g_l...
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Object instance holds common fields that most gnucash objects use.
gnc_commodity * gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
allocate and copy
This is a locally installed quote source that gnucash knows nothing about.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
Definition: gnc-numeric.h:190
A gnc_commodity_namespace is an collection of commodities.
void gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity.
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition: qofobject.h:64
QuoteSourceType
The quote source type enum account types are used to determine how the transaction data in the accoun...
gnc_quote_source * gnc_quote_source_lookup_by_ti(QuoteSourceType type, gint index)
Given the type/index of a quote source, find the data structure identified by this pair...
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity, based on user input.
gnc_commodity_namespace * gnc_commodity_table_add_namespace(gnc_commodity_table *table, const char *name_space, QofBook *book)
This function adds a new string to the list of commodity namespaces.
void qof_collection_foreach(const QofCollection *col, QofInstanceForeachCB cb_func, gpointer user_data)
Call the callback for each entity in the collection.
Definition: qofid.cpp:321
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
void qof_instance_get_kvp(QofInstance *, GValue *value, unsigned count,...)
Retrieves the contents of a KVP slot into a provided GValue.
void gnc_commodity_set_user_symbol(gnc_commodity *cm, const char *user_symbol)
Set a user-defined symbol for the specified commodity.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
MonetaryList * gnc_monetary_list_delete_zeros(MonetaryList *list)
Delete all entries in the list that have zero value.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
Set the automatic price quote source for the specified commodity.
gint gnc_quote_source_num_entries(QuoteSourceType type)
Return the number of entries for a given type of quote source.
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
gboolean gnc_commodity_table_register(void)
You should probably not be using gnc_commodity_table_register() It is an internal routine for registe...
void gnc_commodity_increment_usage_count(gnc_commodity *cm)
Increment a commodity&#39;s internal counter that tracks how many accounts are using that commodity...
void gnc_commodity_set_cusip(gnc_commodity *cm, const char *cusip)
Set the &#39;exchange code&#39; for the specified commodity.
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
Create a new commodity.
int gnc_commodity_table_has_namespace(const gnc_commodity_table *table, const char *name_space)
Test to see if the indicated namespace exits in the commodity table.
void gnc_commodity_table_delete_namespace(gnc_commodity_table *table, const char *name_space)
This function deletes a string from the list of commodity namespaces.
gboolean gnc_commodity_namespace_is_iso(const char *name_space)
Checks to see if the specified commodity namespace is the namespace for ISO 4217 currencies.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
#define GNC_COMMODITY_NS_LEGACY
The commodity namespace definitions are used to tag a commodity by its type, or a stocks by the excha...
void qof_book_set_data(QofBook *book, const gchar *key, gpointer data)
The qof_book_set_data() allows arbitrary pointers to structs to be stored in QofBook.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
const char * gnc_commodity_get_nice_symbol(const gnc_commodity *cm)
Retrieve a symbol for the specified commodity, suitable for display to the user.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:261
void gnc_quote_source_set_fq_installed(const char *version_string, const std::vector< std::string > &sources_list)
Update gnucash internal tables based on what Finance::Quote sources are installed.
const char * gnc_quote_source_fq_version(void)
This function returns the version of the Finance::Quote module installed on a user&#39;s computer...
Generic api to store and retrieve preferences.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
gnc_quote_source * gnc_quote_source_lookup_by_internal(const char *name)
Given the internal (gnucash or F::Q) name of a quote source, find the data structure identified by th...
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the &#39;print&#39; name for the specified commodity.
int gnc_commodity_compare(const gnc_commodity *a, const gnc_commodity *b)
This routine returns 0 if the two commodities are equal, 1 otherwise.
void gnc_commodity_set_fullname(gnc_commodity *cm, const char *fullname)
Set the full name for the specified commodity.
This quote source may pull from multiple web sites.
gnc_quote_source * gnc_commodity_get_quote_source(const gnc_commodity *cm)
Retrieve the automatic price quote source for the specified commodity.
gnc_commodity_namespace * gnc_commodity_table_find_namespace(const gnc_commodity_table *table, const char *name_space)
This function finds a commodity namespace in the set of existing commodity namespaces.
void gnc_commodity_set_mnemonic(gnc_commodity *cm, const char *mnemonic)
Set the mnemonic for the specified commodity.
const char * gnc_commodity_get_default_symbol(const gnc_commodity *cm)
Retrieve the default symbol for the specified commodity.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
CommodityList * gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table *table)
This function returns a list of commodities for which price quotes should be retrieved.
gnc_commodity_table * gnc_commodity_table_new(void)
You probably shouldn&#39;t be using gnc_commodity_table_new() directly, it&#39;s for internal use only...
gint gnc_quote_source_get_index(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the index of this particular quote source within its ...
gnc_commodity_namespace * gnc_commodity_get_namespace_ds(const gnc_commodity *cm)
Retrieve the namespace data structure for the specified commodity.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the &#39;unique&#39; name for the specified commodity.
MonetaryList * gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
Add a gnc_monetary to the list.
void gnc_commodity_table_remove(gnc_commodity_table *table, gnc_commodity *comm)
Remove a commodity from the commodity table.
void gnc_commodity_set_namespace(gnc_commodity *cm, const char *name_space)
Set the namespace for the specified commodity.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
guint gnc_commodity_table_get_size(const gnc_commodity_table *tbl)
Returns the number of commodities in the commodity table.
const char * gnc_quote_source_get_internal_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the internal name of this quote source.
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
Definition: qofobject.cpp:299
An article that is bought and sold.
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition: qofevent.cpp:231
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:247
The type used to store guids in C.
Definition: guid.h:75
void gnc_commodity_copy(gnc_commodity *dest, const gnc_commodity *src)
Copy src into dest.
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
Retrieves arbitrary pointers to structs stored by qof_book_set_data.
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
gnc_commodity * gnc_commodity_obtain_twin(const gnc_commodity *from, QofBook *book)
Given the commodity &#39;findlike&#39;, this routine will find and return the equivalent commodity (commodity...
Commodity handling public routines.
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
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.
gboolean gnc_quote_source_fq_installed(void)
This function indicates whether or not the Finance::Quote module is installed on a user&#39;s computer...