GnuCash  5.6-150-g038405b370+
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, NC_("FQ Source", "Alphavantage"), "alphavantage" },
192  { false, SOURCE_SINGLE, NC_("FQ Source", "Association of Mutual Funds in India"), "amfiindia" },
193  { false, SOURCE_SINGLE, NC_("FQ Source", "Athens Exchange Group, GR"), "asegr" },
194  { false, SOURCE_SINGLE, NC_("FQ Source", "Australian Stock Exchange, AU"), "asx" },
195  { false, SOURCE_SINGLE, NC_("FQ Source", "Italian Stock Exchange, IT"), "borsa_italiana" },
196  { false, SOURCE_SINGLE, NC_("FQ Source", "BSE India, IN"), "bseindia" },
197  { false, SOURCE_SINGLE, NC_("FQ Source", "Bucharest Stock Exchange, RO"), "bvb" },
198  { false, SOURCE_SINGLE, NC_("FQ Source", "Colombo Stock Exchange, LK"), "cse" },
199  { false, SOURCE_SINGLE, NC_("FQ Source", "comdirect, DE"), "comdirect" },
200  { false, SOURCE_SINGLE, NC_("FQ Source", "Consors Bank, DE"), "consorsbank" },
201  { false, SOURCE_SINGLE, NC_("FQ Source", "Deka Investments, DE"), "deka" },
202  { false, SOURCE_SINGLE, NC_("FQ Source", "Financial Times Funds service, GB"), "ftfunds" },
203  { false, SOURCE_SINGLE, NC_("FQ Source", "Finanzpartner, DE"), "finanzpartner" },
204  { false, SOURCE_SINGLE, NC_("FQ Source", "Finnhub, US"), "finnhub" },
205  { false, SOURCE_SINGLE, NC_("FQ Source", "FondsWeb, DE"), "fondsweb" },
206  { false, SOURCE_SINGLE, NC_("FQ Source", "GoldMoney precious metals"), "goldmoney" },
207  { false, SOURCE_SINGLE, NC_("FQ Source", "Google Web, US Stocks"), "googleweb" },
208  { false, SOURCE_SINGLE, NC_("FQ Source", "Market Watch"), "marketwatch" },
209  { false, SOURCE_SINGLE, NC_("FQ Source", "Morningstar, JP"), "morningstarjp" },
210  { false, SOURCE_SINGLE, NC_("FQ Source", "Motley Fool"), "fool" },
211  { false, SOURCE_SINGLE, NC_("FQ Source", "New Zealand stock eXchange, NZ"), "nzx" },
212  { false, SOURCE_SINGLE, NC_("FQ Source", "NSE (National Stock Exchange), IN"), "nseindia" },
213  { false, SOURCE_SINGLE, NC_("FQ Source", "OnVista, DE"), "onvista"},
214  { false, SOURCE_SINGLE, NC_("FQ Source", "Paris Stock Exchange/Boursorama, FR"), "bourso" },
215  { false, SOURCE_SINGLE, NC_("FQ Source", "S-Investor, DE"), "sinvestor"},
216  { false, SOURCE_SINGLE, NC_("FQ Source", "Sharenet, ZA"), "za" },
217  { false, SOURCE_SINGLE, NC_("FQ Source", "SIX Swiss Exchange shares, CH"), "six" },
218  { false, SOURCE_SINGLE, NC_("FQ Source", "StockData"), "stockdata" },
219  { false, SOURCE_SINGLE, NC_("FQ Source", "Stooq, PL"), "stooq" },
220  { false, SOURCE_SINGLE, NC_("FQ Source", "Swiss Fund Data AG, CH"), "swissfunddata" },
221  { false, SOURCE_SINGLE, NC_("FQ Source", "Tesouro Direto bonds, BR"), "tesouro_direto" },
222  { false, SOURCE_SINGLE, NC_("FQ Source", "Toronto Stock eXchange, CA"), "tsx" },
223  { false, SOURCE_SINGLE, NC_("FQ Source", "Tradegate, DE"), "tradegate" },
224  { false, SOURCE_SINGLE, NC_("FQ Source", "Treasury Direct bonds, US"), "treasurydirect" },
225  { false, SOURCE_SINGLE, NC_("FQ Source", "Twelve Data"), "twelvedata" },
226  { false, SOURCE_SINGLE, NC_("FQ Source", "Union Investment, DE"), "unionfunds" },
227  { false, SOURCE_SINGLE, NC_("FQ Source", "US Savings Bonds, US"), "usbonds" },
228  { false, SOURCE_SINGLE, NC_("FQ Source", "US Govt. Thrift Savings Plan"), "tsp" },
229  { false, SOURCE_SINGLE, NC_("FQ Source", "XETRA, DE"), "xetra" },
230  { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo as JSON"), "yahoo_json" },
231  { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo Web"), "yahooweb" },
232  { false, SOURCE_SINGLE, NC_("FQ Source", "YH Finance (FinanceAPI)"), "financeapi" },
233 };
234 
235 // Finance::Quote defines these as failover methods
236 static QuoteSourceList multiple_quote_sources =
237 {
238  { false, SOURCE_MULTI, NC_("FQ Source", "Canada (Alphavantage, TMX)"), "canada" },
239  { false, SOURCE_MULTI, NC_("FQ Source", "Europe (ASEGR, Bourso, …)"), "europe" },
240  { false, SOURCE_MULTI, NC_("FQ Source", "India (BSEIndia, NSEIndia)"), "india"},
241  { false, SOURCE_MULTI, NC_("FQ Source", "Nasdaq (Alphavantage, FinanceAPI, …)"), "nasdaq" },
242  { false, SOURCE_MULTI, NC_("FQ Source", "NYSE (Alphavantage, FinanceAPI, …)"), "nyse" },
243  { false, SOURCE_MULTI, NC_("FQ Source", "U.K. Funds (FTfunds)"), "ukfunds" },
244  { false, SOURCE_MULTI, NC_("FQ Source", "USA (Alphavantage, FinanceAPI, …)"), "usa" },
245 };
246 
247 static QuoteSourceList new_quote_sources;
248 
249 // cannot use map or unordered_map because order must be preserved
250 static const std::vector<std::pair<QuoteSourceType,QuoteSourceList&>> quote_sources_map =
251  {
252  { SOURCE_CURRENCY, currency_quote_sources },
253  { SOURCE_SINGLE, single_quote_sources },
254  { SOURCE_MULTI, multiple_quote_sources },
255  { SOURCE_UNKNOWN, new_quote_sources }
256  };
257 
258 /********************************************************************
259  * gnc_quote_source_fq_installed
260  *
261  * This function indicates whether or not the Finance::Quote module
262  * is installed on a users computer.
263  ********************************************************************/
264 gboolean
266 {
267  return (!fq_version.empty());
268 }
269 
270 
271 /********************************************************************
272  * gnc_quote_source_fq_version
273  *
274  * This function the version of the Finance::Quote module installed
275  * on a user's computer or nullptr if no installation is found.
276  ********************************************************************/
277 const char*
279 {
280  return fq_version.c_str();
281 }
282 
283 static QuoteSourceList&
284 get_quote_source_from_type (QuoteSourceType type)
285 {
286  auto quote_sources_it = std::find_if (quote_sources_map.begin(), quote_sources_map.end(),
287  [type] (const auto& qs) { return type == qs.first; });
288 
289  if (quote_sources_it != quote_sources_map.end())
290  return quote_sources_it->second;
291 
292  PWARN ("Invalid Quote Source %d, returning new_quote_sources", type);
293  return new_quote_sources;
294 }
295 
296 /********************************************************************
297  * gnc_quote_source_num_entries
298  *
299  * Return the number of entries for a given type of price source.
300  ********************************************************************/
302 {
303  return get_quote_source_from_type(type).size();
304 }
305 
306 
307 
308 /********************************************************************
309  * gnc_quote_source_add_new
310  *
311  * Add a new price source. Called when unknown source names are found
312  * either in the F::Q installation (a newly available source) or in
313  * the user's data file (a source that has vanished but needs to be
314  * tracked.)
315  ********************************************************************/
316 gnc_quote_source *
317 gnc_quote_source_add_new (const char *source_name, gboolean supported)
318 {
319  DEBUG("Creating new source %s", (!source_name ? "(null)" : source_name));
320  /* This name can be changed if/when support for this price source is
321  * integrated into gnucash. */
322  /* This name is permanent and must be kept the same if/when support
323  * for this price source is integrated into gnucash (i.e. for a
324  * nice user name). */
325  return &new_quote_sources.emplace_back (supported, SOURCE_UNKNOWN, source_name, source_name);
326 }
327 
328 /********************************************************************
329  * gnc_quote_source_lookup_by_xxx
330  *
331  * Lookup a price source data structure based upon various criteria.
332  ********************************************************************/
333 gnc_quote_source *
335 {
336  ENTER("type/index is %d/%d", type, index);
337  auto& sources = get_quote_source_from_type (type);
338  if ((size_t) index < sources.size())
339  {
340  auto it = std::next(sources.begin(), index);
341  LEAVE("found %s", it->get_user_name());
342  return &*it;
343  }
344 
345  LEAVE("not found");
346  return nullptr;
347 }
348 
349 gnc_quote_source *
351 {
352  if (!name || !*name)
353  return nullptr;
354 
355  for (const auto& [_, sources] : quote_sources_map)
356  {
357  auto source_it = std::find_if (sources.begin(), sources.end(),
358  [name] (const auto& qs)
359  { return (g_strcmp0(name, qs.get_internal_name()) == 0); });
360  if (source_it != sources.end())
361  return &(*source_it);
362  }
363 
364  DEBUG("gnc_quote_source_lookup_by_internal: Unknown source %s", name);
365  return nullptr;
366 }
367 
368 /********************************************************************
369  * gnc_quote_source_get_xxx
370  *
371  * Accessor functions - get functions only. There are no set functions.
372  ********************************************************************/
374 gnc_quote_source_get_type (const gnc_quote_source *source)
375 {
376  ENTER("%p", source);
377  if (!source)
378  {
379  LEAVE("bad source");
380  return SOURCE_SINGLE;
381  }
382 
383  LEAVE("type is %d", source->get_type());
384  return source->get_type();
385 }
386 
387 gint
388 gnc_quote_source_get_index (const gnc_quote_source *source)
389 {
390  if (!source)
391  {
392  PWARN ("bad source");
393  return 0;
394  }
395 
396  auto& sources = get_quote_source_from_type (source->get_type());
397  auto is_source = [&source](const auto& findif_source)
398  { return &findif_source == source; };
399 
400  auto iter = std::find_if (sources.begin(), sources.end(), is_source);
401  if (iter != sources.end())
402  return std::distance (sources.begin(), iter);
403 
404  PWARN ("couldn't locate source");
405  return 0;
406 }
407 
408 gboolean
409 gnc_quote_source_get_supported (const gnc_quote_source *source)
410 {
411  ENTER("%p", source);
412  if (!source)
413  {
414  LEAVE("bad source");
415  return FALSE;
416  }
417 
418  LEAVE("%s supported", source && source->get_supported() ? "" : "not ");
419  return source->get_supported();
420 }
421 
422 const char *
423 gnc_quote_source_get_user_name (const gnc_quote_source *source)
424 {
425  ENTER("%p", source);
426  if (!source)
427  {
428  LEAVE("bad source");
429  return nullptr;
430  }
431  LEAVE("user name %s", source->get_user_name());
432  return source->get_user_name();
433 }
434 
435 const char *
436 gnc_quote_source_get_internal_name (const gnc_quote_source *source)
437 {
438  ENTER("%p", source);
439  if (!source)
440  {
441  LEAVE("bad source");
442  return nullptr;
443  }
444  LEAVE("internal name %s", source->get_internal_name());
445  return source->get_internal_name();
446 }
447 
448 
449 /********************************************************************
450  * gnc_quote_source_set_fq_installed
451  *
452  * Update gnucash internal tables on what Finance::Quote sources are
453  * installed.
454  ********************************************************************/
455 void
456 gnc_quote_source_set_fq_installed (const char* version_string,
457  const std::vector<std::string>& sources_list)
458 {
459  ENTER(" ");
460 
461  if (sources_list.empty())
462  return;
463 
464  if (version_string)
465  fq_version = version_string;
466  else
467  fq_version.clear();
468 
469  for (const auto& source_name_str : sources_list)
470  {
471  auto source_name = source_name_str.c_str();
472  auto source = gnc_quote_source_lookup_by_internal(source_name);
473 
474  if (source)
475  {
476  DEBUG("Found source %s: %s", source_name, source->get_user_name());
477  source->set_supported (true);
478  continue;
479  }
480 
481  gnc_quote_source_add_new(source_name, TRUE);
482  }
483  LEAVE(" ");
484 }
485 
486 /********************************************************************
487  * QoF Helpers
488  ********************************************************************/
489 
490 void
491 gnc_commodity_begin_edit (gnc_commodity *cm)
492 {
493  qof_begin_edit(&cm->inst);
494 }
495 
496 static void commit_err (QofInstance *inst, QofBackendError errcode)
497 {
498  PERR ("Failed to commit: %d", errcode);
499  gnc_engine_signal_commit_error( errcode );
500 }
501 
502 static void noop (QofInstance *inst) {}
503 
504 static void
505 comm_free(QofInstance* inst)
506 {
507  commodity_free( GNC_COMMODITY(inst) );
508 }
509 
510 void
511 gnc_commodity_commit_edit (gnc_commodity *cm)
512 {
513  if (!qof_commit_edit (QOF_INSTANCE(cm))) return;
514  qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free);
515 }
516 
517 /********************************************************************
518  * gnc_commodity_new
519  ********************************************************************/
520 
521 static void
522 mark_commodity_dirty (gnc_commodity *cm)
523 {
524  qof_instance_set_dirty(&cm->inst);
525  qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, nullptr);
526 }
527 
528 static void
529 reset_printname(gnc_commodityPrivate *priv)
530 {
531  g_free(priv->printname);
532  priv->printname = g_strdup_printf("%s (%s)",
533  priv->mnemonic ? priv->mnemonic : "",
534  priv->fullname ? priv->fullname : "");
535 }
536 
537 static void
538 reset_unique_name(gnc_commodityPrivate *priv)
539 {
540  gnc_commodity_namespace *ns;
541 
542  g_free(priv->unique_name);
543  ns = priv->name_space;
544  priv->unique_name = g_strdup_printf("%s::%s",
545  ns ? ns->name : "",
546  priv->mnemonic ? priv->mnemonic : "");
547 }
548 
549 /* GObject Initialization */
550 G_DEFINE_TYPE_WITH_PRIVATE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE)
551 
552 static void
553 gnc_commodity_init(gnc_commodity* com)
554 {
555  gnc_commodityPrivate* priv;
556 
557  priv = GET_PRIVATE(com);
558 
559  priv->name_space = nullptr;
560  priv->fullname = CACHE_INSERT("");
561  priv->mnemonic = CACHE_INSERT("");
562  priv->cusip = CACHE_INSERT("");
563  priv->fraction = 10000;
564  priv->quote_flag = 0;
565  priv->quote_source = nullptr;
566  priv->quote_tz = CACHE_INSERT("");
567 
568  reset_printname(priv);
569  reset_unique_name(priv);
570 }
571 
572 static void
573 gnc_commodity_dispose(GObject *comp)
574 {
575  G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp);
576 }
577 
578 static void
579 gnc_commodity_finalize(GObject* comp)
580 {
581  G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp);
582 }
583 /* Note that g_value_set_object() refs the object, as does
584  * g_object_get(). But g_object_get() only unrefs once when it disgorges
585  * the object, leaving an unbalanced ref, which leaks. So instead of
586  * using g_value_set_object(), use g_value_take_object() which doesn't
587  * ref the object when used in get_property().
588  */
589 static void
590 gnc_commodity_get_property (GObject *object,
591  guint prop_id,
592  GValue *value,
593  GParamSpec *pspec)
594 {
595  gnc_commodity *commodity;
596  gnc_commodityPrivate* priv;
597 
598  g_return_if_fail(GNC_IS_COMMODITY(object));
599 
600  commodity = GNC_COMMODITY(object);
601  priv = GET_PRIVATE(commodity);
602  switch (prop_id)
603  {
604  case PROP_NAMESPACE:
605  g_value_take_object(value, priv->name_space);
606  break;
607  case PROP_FULL_NAME:
608  g_value_set_string(value, priv->fullname);
609  break;
610  case PROP_MNEMONIC:
611  g_value_set_string(value, priv->mnemonic);
612  break;
613  case PROP_PRINTNAME:
614  g_value_set_string(value, priv->printname);
615  break;
616  case PROP_CUSIP:
617  g_value_set_string(value, priv->cusip);
618  break;
619  case PROP_FRACTION:
620  g_value_set_int(value, priv->fraction);
621  break;
622  case PROP_UNIQUE_NAME:
623  g_value_set_string(value, priv->unique_name);
624  break;
625  case PROP_QUOTE_FLAG:
626  g_value_set_boolean(value, priv->quote_flag);
627  break;
628  case PROP_QUOTE_SOURCE:
629  g_value_set_pointer(value, priv->quote_source);
630  break;
631  case PROP_QUOTE_TZ:
632  g_value_set_string(value, priv->quote_tz);
633  break;
634  default:
635  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
636  break;
637  }
638 }
639 
640 static void
641 gnc_commodity_set_property (GObject *object,
642  guint prop_id,
643  const GValue *value,
644  GParamSpec *pspec)
645 {
646  gnc_commodity *commodity;
647 
648  g_return_if_fail(GNC_IS_COMMODITY(object));
649 
650  commodity = GNC_COMMODITY(object);
651  g_assert (qof_instance_get_editlevel(commodity));
652 
653  switch (prop_id)
654  {
655  case PROP_NAMESPACE:
656  gnc_commodity_set_namespace(commodity, static_cast<const char*>(g_value_get_object(value)));
657  break;
658  case PROP_FULL_NAME:
659  gnc_commodity_set_fullname(commodity, g_value_get_string(value));
660  break;
661  case PROP_MNEMONIC:
662  gnc_commodity_set_mnemonic(commodity, g_value_get_string(value));
663  break;
664  case PROP_CUSIP:
665  gnc_commodity_set_cusip(commodity, g_value_get_string(value));
666  break;
667  case PROP_FRACTION:
668  gnc_commodity_set_fraction(commodity, g_value_get_int(value));
669  break;
670  case PROP_QUOTE_FLAG:
671  gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value));
672  break;
673  case PROP_QUOTE_SOURCE:
674  gnc_commodity_set_quote_source(commodity, static_cast<gnc_quote_source*>(g_value_get_pointer(value)));
675  break;
676  case PROP_QUOTE_TZ:
677  gnc_commodity_set_quote_tz(commodity, g_value_get_string(value));
678  break;
679  default:
680  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
681  break;
682  }
683 }
684 static void
685 gnc_commodity_class_init(struct _GncCommodityClass* klass)
686 {
687  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
688 
689  gobject_class->dispose = gnc_commodity_dispose;
690  gobject_class->finalize = gnc_commodity_finalize;
691  gobject_class->set_property = gnc_commodity_set_property;
692  gobject_class->get_property = gnc_commodity_get_property;
693 
694  g_object_class_install_property(gobject_class,
695  PROP_NAMESPACE,
696  g_param_spec_object ("namespace",
697  "Namespace",
698  "The namespace field denotes the "
699  "namespace for this commodity, either "
700  "a currency or symbol from a quote source.",
701  GNC_TYPE_COMMODITY_NAMESPACE,
702  G_PARAM_READWRITE));
703  g_object_class_install_property(gobject_class,
704  PROP_FULL_NAME,
705  g_param_spec_string ("fullname",
706  "Full Commodity Name",
707  "The fullname is the official full name of"
708  "the currency.",
709  nullptr,
710  G_PARAM_READWRITE));
711  g_object_class_install_property(gobject_class,
712  PROP_MNEMONIC,
713  g_param_spec_string ("mnemonic",
714  "Commodity Mnemonic",
715  "The mnemonic is the official abbreviated"
716  "designation for the currency.",
717  nullptr,
718  G_PARAM_READWRITE));
719  g_object_class_install_property(gobject_class,
720  PROP_PRINTNAME,
721  g_param_spec_string ("printname",
722  "Commodity Print Name",
723  "Printable form of the commodity name.",
724  nullptr,
725  G_PARAM_READABLE));
726  g_object_class_install_property(gobject_class,
727  PROP_CUSIP,
728  g_param_spec_string ("cusip",
729  "Commodity CUSIP Code",
730  "?????",
731  nullptr,
732  G_PARAM_READWRITE));
733  g_object_class_install_property(gobject_class,
734  PROP_FRACTION,
735  g_param_spec_int ("fraction",
736  "Fraction",
737  "The fraction is the number of sub-units that "
738  "the basic commodity can be divided into.",
739  1,
741  1,
742  G_PARAM_READWRITE));
743  g_object_class_install_property(gobject_class,
744  PROP_UNIQUE_NAME,
745  g_param_spec_string ("unique-name",
746  "Commodity Unique Name",
747  "Unique form of the commodity name which combines "
748  "the namespace name and the commodity name.",
749  nullptr,
750  G_PARAM_READABLE));
751  g_object_class_install_property(gobject_class,
752  PROP_QUOTE_FLAG,
753  g_param_spec_boolean ("quote_flag",
754  "Quote Flag",
755  "TRUE if prices are to be downloaded for this "
756  "commodity from a quote source.",
757  FALSE,
758  G_PARAM_READWRITE));
759  g_object_class_install_property(gobject_class,
760  PROP_QUOTE_SOURCE,
761  g_param_spec_pointer("quote-source",
762  "Quote Source",
763  "The quote source from which prices are downloaded.",
764  G_PARAM_READWRITE));
765  g_object_class_install_property(gobject_class,
766  PROP_QUOTE_TZ,
767  g_param_spec_string ("quote-tz",
768  "Commodity Quote Timezone",
769  "?????",
770  nullptr,
771  G_PARAM_READWRITE));
772 }
773 
774 gnc_commodity *
775 gnc_commodity_new(QofBook *book, const char * fullname,
776  const char * name_space, const char * mnemonic,
777  const char * cusip, int fraction)
778 {
779  auto retval = GNC_COMMODITY(g_object_new(GNC_TYPE_COMMODITY, nullptr));
780 
781  qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book);
782  gnc_commodity_begin_edit(retval);
783 
784  if ( name_space != nullptr )
785  {
786  /* Prevent setting anything except template in namespace template. */
787  if (g_strcmp0 (name_space, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
788  g_strcmp0 (mnemonic, "template") != 0)
789  {
790  PWARN("Converting commodity %s from namespace template to "
791  "namespace User", mnemonic);
792  name_space = "User";
793  }
794  gnc_commodity_set_namespace(retval, name_space);
795  if (gnc_commodity_namespace_is_iso(name_space))
796  {
799  }
800  }
801  gnc_commodity_set_fullname(retval, fullname);
802  gnc_commodity_set_mnemonic(retval, mnemonic);
803  gnc_commodity_set_cusip(retval, cusip);
804  gnc_commodity_set_fraction(retval, fraction);
805  mark_commodity_dirty (retval);
806  gnc_commodity_commit_edit(retval);
807 
808  qof_event_gen (&retval->inst, QOF_EVENT_CREATE, nullptr);
809 
810  return retval;
811 }
812 
813 
814 /********************************************************************
815  * gnc_commodity_destroy
816  ********************************************************************/
817 
818 static void
819 commodity_free(gnc_commodity * cm)
820 {
821  QofBook *book;
822  gnc_commodity_table *table;
823  gnc_commodityPrivate* priv;
824 
825  if (!cm) return;
826 
827  book = qof_instance_get_book(&cm->inst);
830  priv = GET_PRIVATE(cm);
831 
832  qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, nullptr);
833 
834  /* Set at creation */
835  CACHE_REMOVE (priv->fullname);
836  CACHE_REMOVE (priv->cusip);
837  CACHE_REMOVE (priv->mnemonic);
838  CACHE_REMOVE (priv->quote_tz);
839  priv->name_space = nullptr;
840 
841  /* Set through accessor functions */
842  priv->quote_source = nullptr;
843 
844  /* Automatically generated */
845  g_free(priv->printname);
846  priv->printname = nullptr;
847 
848  g_free(priv->unique_name);
849  priv->unique_name = nullptr;
850 
851 #ifdef ACCOUNTS_CLEANED_UP
852  /* Account objects are not actually cleaned up when a book is closed (in fact
853  * a memory leak), but commodities are, so in currently this warning gets hit
854  * quite frequently. Disable the check until cleaning up of accounts objects
855  * on close is implemented. */
856  if (priv->usage_count != 0)
857  {
858  PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm,
859  priv->usage_count);
860  }
861 #endif
862 
863  /* qof_instance_release (&cm->inst); */
864  g_object_unref(cm);
865 }
866 
867 void
868 gnc_commodity_destroy(gnc_commodity * cm)
869 {
870  gnc_commodity_begin_edit(cm);
871  qof_instance_set_destroying(cm, TRUE);
872  gnc_commodity_commit_edit(cm);
873 }
874 
875 void
876 gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src)
877 {
878  gnc_commodityPrivate* src_priv = GET_PRIVATE(src);
879  gnc_commodityPrivate* dest_priv = GET_PRIVATE(dest);
880 
881  gnc_commodity_set_fullname (dest, src_priv->fullname);
882  gnc_commodity_set_mnemonic (dest, src_priv->mnemonic);
883  dest_priv->name_space = src_priv->name_space;
884  gnc_commodity_set_fraction (dest, src_priv->fraction);
885  gnc_commodity_set_cusip (dest, src_priv->cusip);
886  gnc_commodity_set_quote_flag (dest, src_priv->quote_flag);
888  gnc_commodity_set_quote_tz (dest, src_priv->quote_tz);
889  qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
890 }
891 
892 gnc_commodity *
893 gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
894 {
895  gnc_commodityPrivate* src_priv;
896  gnc_commodityPrivate* dest_priv;
897 
898  auto dest = GNC_COMMODITY (g_object_new(GNC_TYPE_COMMODITY, nullptr));
899  qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book);
900  src_priv = GET_PRIVATE(src);
901  dest_priv = GET_PRIVATE(dest);
902 
903  dest_priv->fullname = CACHE_INSERT(src_priv->fullname);
904  dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic);
905  dest_priv->cusip = CACHE_INSERT(src_priv->cusip);
906  dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz);
907 
908  dest_priv->name_space = src_priv->name_space;
909 
910  dest_priv->fraction = src_priv->fraction;
911  dest_priv->quote_flag = src_priv->quote_flag;
912 
914 
915  qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
916 
917  reset_printname(dest_priv);
918  reset_unique_name(dest_priv);
919 
920  return dest;
921 }
922 
923 /********************************************************************
924  * gnc_commodity_get_mnemonic
925  ********************************************************************/
926 
927 const char *
928 gnc_commodity_get_mnemonic(const gnc_commodity * cm)
929 {
930  if (!cm) return nullptr;
931  return GET_PRIVATE(cm)->mnemonic;
932 }
933 
934 /********************************************************************
935  * gnc_commodity_get_printname
936  ********************************************************************/
937 
938 const char *
939 gnc_commodity_get_printname(const gnc_commodity * cm)
940 {
941  if (!cm) return nullptr;
942  return GET_PRIVATE(cm)->printname;
943 }
944 
945 
946 /********************************************************************
947  * gnc_commodity_get_namespace
948  ********************************************************************/
949 
950 const char *
951 gnc_commodity_get_namespace(const gnc_commodity * cm)
952 {
953  if (!cm) return nullptr;
954  return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
955 }
956 
957 gnc_commodity_namespace *
958 gnc_commodity_get_namespace_ds(const gnc_commodity * cm)
959 {
960  if (!cm) return nullptr;
961  return GET_PRIVATE(cm)->name_space;
962 }
963 
964 /********************************************************************
965  * gnc_commodity_get_fullname
966  ********************************************************************/
967 
968 const char *
969 gnc_commodity_get_fullname(const gnc_commodity * cm)
970 {
971  if (!cm) return nullptr;
972  return GET_PRIVATE(cm)->fullname;
973 }
974 
975 
976 /********************************************************************
977  * gnc_commodity_get_unique_name
978  ********************************************************************/
979 
980 const char *
981 gnc_commodity_get_unique_name(const gnc_commodity * cm)
982 {
983  if (!cm) return nullptr;
984  return GET_PRIVATE(cm)->unique_name;
985 }
986 
987 
988 /********************************************************************
989  * gnc_commodity_get_cusip
990  ********************************************************************/
991 
992 const char *
993 gnc_commodity_get_cusip(const gnc_commodity * cm)
994 {
995  if (!cm) return nullptr;
996  return GET_PRIVATE(cm)->cusip;
997 }
998 
999 /********************************************************************
1000  * gnc_commodity_get_fraction
1001  ********************************************************************/
1002 
1003 int
1004 gnc_commodity_get_fraction(const gnc_commodity * cm)
1005 {
1006  if (!cm) return 0;
1007  return GET_PRIVATE(cm)->fraction;
1008 }
1009 
1010 /********************************************************************
1011  * gnc_commodity_get_auto_quote_control_flag
1012  ********************************************************************/
1013 
1014 gboolean
1015 gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
1016 {
1017  if (!cm) return FALSE;
1018  auto str{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE (cm), {"auto_quote_control"})};
1019  return !str || g_strcmp0 (*str, "false");
1020 }
1021 
1022 /********************************************************************
1023  * gnc_commodity_get_quote_flag
1024  ********************************************************************/
1025 
1026 gboolean
1027 gnc_commodity_get_quote_flag(const gnc_commodity *cm)
1028 {
1029  if (!cm) return FALSE;
1030  return (GET_PRIVATE(cm)->quote_flag);
1031 }
1032 
1033 /********************************************************************
1034  * gnc_commodity_get_quote_source
1035  ********************************************************************/
1036 
1037 gnc_quote_source*
1038 gnc_commodity_get_quote_source(const gnc_commodity *cm)
1039 {
1040  gnc_commodityPrivate* priv;
1041 
1042  if (!cm) return nullptr;
1043  priv = GET_PRIVATE(cm);
1044  if (!priv->quote_source && gnc_commodity_is_iso(cm))
1045  return &currency_quote_sources.front();
1046  return priv->quote_source;
1047 }
1048 
1049 gnc_quote_source*
1050 gnc_commodity_get_default_quote_source(const gnc_commodity *cm)
1051 {
1052  if (cm && gnc_commodity_is_iso(cm))
1053  return &currency_quote_sources.front();
1054  /* Should make this a user option at some point. */
1055  return gnc_quote_source_lookup_by_internal("alphavantage");
1056 }
1057 
1058 /********************************************************************
1059  * gnc_commodity_get_quote_tz
1060  ********************************************************************/
1061 
1062 const char*
1063 gnc_commodity_get_quote_tz(const gnc_commodity *cm)
1064 {
1065  if (!cm) return nullptr;
1066  return GET_PRIVATE(cm)->quote_tz;
1067 }
1068 
1069 /********************************************************************
1070  * gnc_commodity_get_user_symbol
1071  ********************************************************************/
1072 const char*
1073 gnc_commodity_get_user_symbol(const gnc_commodity *cm)
1074 {
1075  g_return_val_if_fail (GNC_IS_COMMODITY (cm), nullptr);
1076 
1077  auto sym{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(cm), {"user_symbol"})};
1078  return sym ? *sym : nullptr;
1079 }
1080 
1081 /********************************************************************
1082  * gnc_commodity_get_default_symbol
1083  *******************************************************************/
1084 const char*
1085 gnc_commodity_get_default_symbol(const gnc_commodity *cm)
1086 {
1087  if (!cm) return nullptr;
1088  return GET_PRIVATE(cm)->default_symbol;
1089 }
1090 
1091 /********************************************************************
1092  * gnc_commodity_get_nice_symbol
1093  *******************************************************************/
1094 const char*
1095 gnc_commodity_get_nice_symbol (const gnc_commodity *cm)
1096 {
1097  const char *nice_symbol;
1098  struct lconv *lc;
1099  if (!cm) return nullptr;
1100 
1101  nice_symbol = gnc_commodity_get_user_symbol(cm);
1102  if (nice_symbol && *nice_symbol)
1103  return nice_symbol;
1104 
1105  lc = gnc_localeconv();
1106  nice_symbol = lc->currency_symbol;
1107  if (!g_strcmp0(gnc_commodity_get_mnemonic(cm), lc->int_curr_symbol))
1108  return nice_symbol;
1109 
1110  nice_symbol = gnc_commodity_get_default_symbol(cm);
1111  if (nice_symbol && *nice_symbol)
1112  return nice_symbol;
1113 
1114  return gnc_commodity_get_mnemonic(cm);
1115 }
1116 
1117 /********************************************************************
1118  * gnc_commodity_set_mnemonic
1119  ********************************************************************/
1120 
1121 void
1122 gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic)
1123 {
1124  gnc_commodityPrivate* priv;
1125 
1126  if (!cm) return;
1127  priv = GET_PRIVATE(cm);
1128  if (priv->mnemonic == mnemonic) return;
1129 
1130  gnc_commodity_begin_edit(cm);
1131  CACHE_REMOVE (priv->mnemonic);
1132  priv->mnemonic = CACHE_INSERT(mnemonic);
1133 
1134  mark_commodity_dirty (cm);
1135  reset_printname(priv);
1136  reset_unique_name(priv);
1137  gnc_commodity_commit_edit(cm);
1138 }
1139 
1140 /********************************************************************
1141  * gnc_commodity_set_namespace
1142  ********************************************************************/
1143 
1144 void
1145 gnc_commodity_set_namespace(gnc_commodity * cm, const char * name_space)
1146 {
1147  QofBook *book;
1148  gnc_commodity_table *table;
1149  gnc_commodity_namespace *nsp;
1150  gnc_commodityPrivate* priv;
1151 
1152  if (!cm) return;
1153  priv = GET_PRIVATE(cm);
1154  book = qof_instance_get_book (&cm->inst);
1156  nsp = gnc_commodity_table_add_namespace(table, name_space, book);
1157  if (priv->name_space == nsp)
1158  return;
1159 
1160  gnc_commodity_begin_edit(cm);
1161  priv->name_space = nsp;
1162  if (nsp->iso4217)
1163  priv->quote_source = gnc_quote_source_lookup_by_internal("currency");
1164  mark_commodity_dirty(cm);
1165  reset_printname(priv);
1166  reset_unique_name(priv);
1167  gnc_commodity_commit_edit(cm);
1168 }
1169 
1170 /********************************************************************
1171  * gnc_commodity_set_fullname
1172  ********************************************************************/
1173 
1174 void
1175 gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname)
1176 {
1177  gnc_commodityPrivate* priv;
1178 
1179  if (!cm) return;
1180  priv = GET_PRIVATE(cm);
1181  if (priv->fullname == fullname) return;
1182 
1183  CACHE_REMOVE (priv->fullname);
1184  priv->fullname = CACHE_INSERT (fullname);
1185 
1186  gnc_commodity_begin_edit(cm);
1187  mark_commodity_dirty(cm);
1188  reset_printname(priv);
1189  gnc_commodity_commit_edit(cm);
1190 }
1191 
1192 /********************************************************************
1193  * gnc_commodity_set_cusip
1194  ********************************************************************/
1195 
1196 void
1197 gnc_commodity_set_cusip(gnc_commodity * cm,
1198  const char * cusip)
1199 {
1200  gnc_commodityPrivate* priv;
1201 
1202  if (!cm) return;
1203 
1204  priv = GET_PRIVATE(cm);
1205  if (priv->cusip == cusip) return;
1206 
1207  gnc_commodity_begin_edit(cm);
1208  CACHE_REMOVE (priv->cusip);
1209  priv->cusip = CACHE_INSERT (cusip);
1210  mark_commodity_dirty(cm);
1211  gnc_commodity_commit_edit(cm);
1212 }
1213 
1214 /********************************************************************
1215  * gnc_commodity_set_fraction
1216  ********************************************************************/
1217 
1218 void
1219 gnc_commodity_set_fraction(gnc_commodity * cm, int fraction)
1220 {
1221  if (!cm) return;
1222  gnc_commodity_begin_edit(cm);
1223  GET_PRIVATE(cm)->fraction = fraction;
1224  mark_commodity_dirty(cm);
1225  gnc_commodity_commit_edit(cm);
1226 }
1227 
1228 /********************************************************************
1229  * gnc_commodity_set_auto_quote_control_flag
1230  ********************************************************************/
1231 
1232 void
1233 gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
1234  const gboolean flag)
1235 {
1236  ENTER ("(cm=%p, flag=%d)", cm, flag);
1237 
1238  if (!cm)
1239  {
1240  LEAVE("");
1241  return;
1242  }
1243  gnc_commodity_begin_edit(cm);
1244  auto val = flag ? std::nullopt : std::make_optional<const char*>(g_strdup("false"));
1245  qof_instance_set_path_kvp<const char*> (QOF_INSTANCE (cm), val, {"auto_quote_control"});
1246  mark_commodity_dirty(cm);
1247  gnc_commodity_commit_edit(cm);
1248  LEAVE("");
1249 }
1250 
1251 /********************************************************************
1252  * gnc_commodity_user_set_quote_flag
1253  ********************************************************************/
1254 
1255 void
1256 gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1257 {
1258  gnc_commodityPrivate* priv;
1259 
1260  ENTER ("(cm=%p, flag=%d)", cm, flag);
1261 
1262  if (!cm)
1263  {
1264  LEAVE("");
1265  return;
1266  }
1267 
1268  priv = GET_PRIVATE(cm);
1269  gnc_commodity_begin_edit(cm);
1270  gnc_commodity_set_quote_flag(cm, flag);
1271  if (gnc_commodity_is_iso(cm))
1272  {
1273  /* For currencies, disable auto quote control if the quote flag is being
1274  * changed from its default value and enable it if the quote flag is being
1275  * reset to its default value. The defaults for the quote flag are
1276  * disabled if no accounts are using the currency, and true otherwise.
1277  * Thus enable auto quote control if flag is FALSE and there are not any
1278  * accounts using this currency OR flag is TRUE and there are accounts
1279  * using this currency; otherwise disable auto quote control */
1280  gnc_commodity_set_auto_quote_control_flag(cm,
1281  (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0)));
1282  }
1283  gnc_commodity_commit_edit(cm);
1284  LEAVE("");
1285 }
1286 
1287 /********************************************************************
1288  * gnc_commodity_set_quote_flag
1289  ********************************************************************/
1290 
1291 void
1292 gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1293 {
1294  ENTER ("(cm=%p, flag=%d)", cm, flag);
1295 
1296  if (!cm) return;
1297  gnc_commodity_begin_edit(cm);
1298  GET_PRIVATE(cm)->quote_flag = flag;
1299  mark_commodity_dirty(cm);
1300  gnc_commodity_commit_edit(cm);
1301  LEAVE(" ");
1302 }
1303 
1304 /********************************************************************
1305  * gnc_commodity_set_quote_source
1306  ********************************************************************/
1307 
1308 void
1309 gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
1310 {
1311  ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->get_internal_name() : "unknown");
1312 
1313  if (!cm) return;
1314  gnc_commodity_begin_edit(cm);
1315  GET_PRIVATE(cm)->quote_source = src;
1316  mark_commodity_dirty(cm);
1317  gnc_commodity_commit_edit(cm);
1318  LEAVE(" ");
1319 }
1320 
1321 /********************************************************************
1322  * gnc_commodity_set_quote_tz
1323  ********************************************************************/
1324 
1325 void
1326 gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
1327 {
1328  gnc_commodityPrivate* priv;
1329 
1330  if (!cm) return;
1331 
1332  ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)");
1333 
1334  priv = GET_PRIVATE(cm);
1335 
1336  if (tz == priv->quote_tz)
1337  {
1338  LEAVE("Already correct TZ");
1339  return;
1340  }
1341 
1342  gnc_commodity_begin_edit(cm);
1343  CACHE_REMOVE (priv->quote_tz);
1344  priv->quote_tz = CACHE_INSERT (tz);
1345  mark_commodity_dirty(cm);
1346  gnc_commodity_commit_edit(cm);
1347  LEAVE(" ");
1348 }
1349 
1350 /********************************************************************
1351  * gnc_commodity_set_user_symbol
1352  ********************************************************************/
1353 
1354 void
1355 gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
1356 {
1357  struct lconv *lc;
1358 
1359  if (!cm) return;
1360 
1361  ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
1362 
1363  lc = gnc_localeconv();
1364  if (!user_symbol || !*user_symbol)
1365  user_symbol = nullptr;
1366  else if (!g_strcmp0(lc->int_curr_symbol, gnc_commodity_get_mnemonic(cm)) &&
1367  !g_strcmp0(lc->currency_symbol, user_symbol))
1368  /* if the user gives the ISO symbol for the locale currency or the
1369  * default symbol, actually remove the user symbol */
1370  user_symbol = nullptr;
1371  else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
1372  user_symbol = nullptr;
1373 
1374  gnc_commodity_begin_edit (cm);
1375 
1376  auto val = user_symbol ? std::make_optional<const char*>(g_strdup(user_symbol)) : std::nullopt;
1377  qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(cm), val, {"user_symbol"});
1378 
1379  mark_commodity_dirty(cm);
1380  gnc_commodity_commit_edit(cm);
1381 
1382  LEAVE(" ");
1383 }
1384 
1385 /********************************************************************
1386  * gnc_commodity_set_default_symbol
1387  * Not made visible in gnc-commodity.h, it is only called from
1388  * iso-4217-currencies.c at startup.
1389  ********************************************************************/
1390 void
1391 gnc_commodity_set_default_symbol(gnc_commodity * cm,
1392  const char * default_symbol)
1393 {
1394  GET_PRIVATE(cm)->default_symbol = default_symbol;
1395 }
1396 
1397 /********************************************************************
1398  * gnc_commodity_increment_usage_count
1399  ********************************************************************/
1400 
1401 void
1403 {
1404  gnc_commodityPrivate* priv;
1405 
1406  ENTER("(cm=%p)", cm);
1407 
1408  if (!cm)
1409  {
1410  LEAVE("");
1411  return;
1412  }
1413 
1414  priv = GET_PRIVATE(cm);
1415 
1416  if ((priv->usage_count == 0) && !priv->quote_flag
1417  && gnc_commodity_get_auto_quote_control_flag(cm)
1418  && gnc_commodity_is_iso(cm))
1419  {
1420  /* compatibility hack - Gnucash 1.8 gets currency quotes when a
1421  non-default currency is assigned to an account. */
1422  gnc_commodity_begin_edit(cm);
1423  gnc_commodity_set_quote_flag(cm, TRUE);
1425  gnc_commodity_get_default_quote_source(cm));
1426  gnc_commodity_commit_edit(cm);
1427  }
1428  priv->usage_count++;
1429  LEAVE("(usage_count=%d)", priv->usage_count);
1430 }
1431 
1432 /********************************************************************
1433  * gnc_commodity_decrement_usage_count
1434  ********************************************************************/
1435 
1436 void
1438 {
1439  gnc_commodityPrivate* priv;
1440 
1441  ENTER("(cm=%p)", cm);
1442 
1443  if (!cm)
1444  {
1445  LEAVE("");
1446  return;
1447  }
1448 
1449  priv = GET_PRIVATE(cm);
1450 
1451  if (priv->usage_count == 0)
1452  {
1453  PWARN("usage_count already zero");
1454  LEAVE("");
1455  return;
1456  }
1457 
1458  priv->usage_count--;
1459  if ((priv->usage_count == 0) && priv->quote_flag
1460  && gnc_commodity_get_auto_quote_control_flag(cm)
1461  && gnc_commodity_is_iso(cm))
1462  {
1463  /* if this is a currency with auto quote control enabled and no more
1464  * accounts reference this currency, disable quote retrieval */
1465  gnc_commodity_set_quote_flag(cm, FALSE);
1466  }
1467  LEAVE("(usage_count=%d)", priv->usage_count);
1468 }
1469 
1470 /********************************************************************\
1471 \********************************************************************/
1472 
1473 
1474 /********************************************************************
1475  * gnc_commodity_equiv
1476  * are two commodities the same?
1477  ********************************************************************/
1478 
1479 gboolean
1480 gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b)
1481 {
1482  gnc_commodityPrivate* priv_a;
1483  gnc_commodityPrivate* priv_b;
1484 
1485  if (a == b) return TRUE;
1486  if (!a || !b) return FALSE;
1487 
1488  priv_a = GET_PRIVATE(a);
1489  priv_b = GET_PRIVATE(b);
1490  if (priv_a->name_space != priv_b->name_space) return FALSE;
1491  if (g_strcmp0(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE;
1492 
1493  return TRUE;
1494 }
1495 
1496 gboolean
1497 gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b)
1498 {
1499  return gnc_commodity_compare(a, b) == 0;
1500 }
1501 
1502 int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b)
1503 {
1504  if (a == b) return 0;
1505  if (a && !b) return 1;
1506  if (b && !a) return -1;
1507  if (auto rv = g_strcmp0 (gnc_commodity_get_unique_name (a), gnc_commodity_get_unique_name (b)))
1508  return rv;
1509  return qof_instance_guid_compare(a, b);
1510 }
1511 
1512 // Used as a callback to g_list_find_custom, it should return 0
1513 // when the commodities match.
1514 int gnc_commodity_compare_void(const void * a, const void * b)
1515 {
1516  return gnc_commodity_compare(GNC_COMMODITY (a), GNC_COMMODITY (b));
1517 }
1518 
1519 /************************************************************
1520  * Namespace functions *
1521  ************************************************************/
1522 const char *
1523 gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns)
1524 {
1525  if (ns == nullptr)
1526  return nullptr;
1527  return ns->name;
1528 }
1529 
1530 const char *
1531 gnc_commodity_namespace_get_gui_name (const gnc_commodity_namespace *ns)
1532 {
1533  if (ns == nullptr)
1534  return nullptr;
1535  if (g_strcmp0 (ns->name, GNC_COMMODITY_NS_CURRENCY) == 0)
1536  return GNC_COMMODITY_NS_ISO_GUI;
1537  return ns->name;
1538 }
1539 
1540 GList *
1541 gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
1542 {
1543  if (!name_space)
1544  return nullptr;
1545 
1546  return g_list_copy (name_space->cm_list);
1547 }
1548 
1549 gboolean
1550 gnc_commodity_namespace_is_iso(const char *name_space)
1551 {
1552  return ((g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0) ||
1553  (g_strcmp0(name_space, GNC_COMMODITY_NS_CURRENCY) == 0));
1554 }
1555 
1556 static const gchar *
1557 gnc_commodity_table_map_namespace(const char * name_space)
1558 {
1559  if (g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0)
1560  return GNC_COMMODITY_NS_CURRENCY;
1561  return name_space;
1562 }
1563 
1564 /********************************************************************
1565  * gnc_commodity_table_new
1566  * make a new commodity table
1567  ********************************************************************/
1568 
1569 gnc_commodity_table *
1571 {
1572  gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1);
1573  retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal);
1574  retval->ns_list = nullptr;
1575  return retval;
1576 }
1577 
1578 /********************************************************************
1579  * book anchor functions
1580  ********************************************************************/
1581 
1582 gnc_commodity_table *
1584 {
1585  if (!book) return nullptr;
1586  return static_cast<gnc_commodity_table*>(qof_book_get_data (book, GNC_COMMODITY_TABLE));
1587 }
1588 
1589 gnc_commodity *
1590 gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book)
1591 {
1592  gnc_commodity *twin;
1593  const char * ucom;
1594  gnc_commodity_table * comtbl;
1595 
1596  if (!from) return nullptr;
1597  comtbl = gnc_commodity_table_get_table (book);
1598  if (!comtbl) return nullptr;
1599 
1600  ucom = gnc_commodity_get_unique_name (from);
1601  twin = gnc_commodity_table_lookup_unique (comtbl, ucom);
1602  if (!twin)
1603  {
1604  twin = gnc_commodity_clone (from, book);
1605  twin = gnc_commodity_table_insert (comtbl, twin);
1606  }
1607  return twin;
1608 }
1609 
1610 /********************************************************************
1611  * gnc_commodity_table_get_size
1612  * get the size of the commodity table
1613  ********************************************************************/
1614 
1615 static void
1616 count_coms(gpointer key, gpointer value, gpointer user_data)
1617 {
1618  GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table;
1619  guint *count = (guint*)user_data;
1620 
1621  if (g_strcmp0((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0)
1622  {
1623  /* don't count default commodities */
1624  return;
1625  }
1626 
1627  if (!value) return;
1628 
1629  *count += g_hash_table_size(tbl);
1630 }
1631 
1632 guint
1633 gnc_commodity_table_get_size(const gnc_commodity_table* tbl)
1634 {
1635  guint count = 0;
1636  g_return_val_if_fail(tbl, 0);
1637  g_return_val_if_fail(tbl->ns_table, 0);
1638 
1639  g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count);
1640 
1641  return count;
1642 }
1643 
1644 /********************************************************************
1645  * gnc_commodity_table_lookup
1646  * locate a commodity by namespace and mnemonic.
1647  ********************************************************************/
1648 
1649 gnc_commodity *
1650 gnc_commodity_table_lookup(const gnc_commodity_table * table,
1651  const char * name_space, const char * mnemonic)
1652 {
1653  gnc_commodity_namespace * nsp = nullptr;
1654 
1655  if (!table || !name_space || !mnemonic) return nullptr;
1656 
1657  nsp = gnc_commodity_table_find_namespace(table, name_space);
1658 
1659  if (nsp)
1660  {
1661  /*
1662  * Backward compatibility support for currencies that have
1663  * recently changed.
1664  */
1665  if (nsp->iso4217)
1666  {
1667  auto it = gnc_new_iso_codes.find (mnemonic);
1668  if (it != gnc_new_iso_codes.end())
1669  mnemonic = it->second.c_str();
1670  }
1671  return GNC_COMMODITY(g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic));
1672  }
1673  else
1674  {
1675  return nullptr;
1676  }
1677 }
1678 
1679 /********************************************************************
1680  * gnc_commodity_table_lookup
1681  * locate a commodity by unique name.
1682  ********************************************************************/
1683 
1684 gnc_commodity *
1685 gnc_commodity_table_lookup_unique(const gnc_commodity_table *table,
1686  const char * unique_name)
1687 {
1688  char *name_space;
1689  char *mnemonic;
1690  gnc_commodity *commodity;
1691 
1692  if (!table || !unique_name) return nullptr;
1693 
1694  name_space = g_strdup (unique_name);
1695  mnemonic = strstr (name_space, "::");
1696  if (!mnemonic)
1697  {
1698  g_free (name_space);
1699  return nullptr;
1700  }
1701 
1702  *mnemonic = '\0';
1703  mnemonic += 2;
1704 
1705  commodity = gnc_commodity_table_lookup (table, name_space, mnemonic);
1706 
1707  g_free (name_space);
1708 
1709  return commodity;
1710 }
1711 
1712 /********************************************************************
1713  * gnc_commodity_table_find_full
1714  * locate a commodity by namespace and printable name
1715  ********************************************************************/
1716 
1717 gnc_commodity *
1718 gnc_commodity_table_find_full(const gnc_commodity_table * table,
1719  const char * name_space,
1720  const char * fullname)
1721 {
1722  gnc_commodity * retval = nullptr;
1723  GList * all;
1724  GList * iterator;
1725 
1726  if (!fullname || (fullname[0] == '\0'))
1727  return nullptr;
1728 
1729  all = gnc_commodity_table_get_commodities(table, name_space);
1730 
1731  for (iterator = all; iterator; iterator = iterator->next)
1732  {
1733  auto commodity = GNC_COMMODITY (iterator->data);
1734  if (!strcmp(fullname,
1735  gnc_commodity_get_printname(commodity)))
1736  {
1737  retval = commodity;
1738  break;
1739  }
1740  }
1741 
1742  g_list_free (all);
1743 
1744  return retval;
1745 }
1746 
1747 
1748 /********************************************************************
1749  * gnc_commodity_table_insert
1750  * add a commodity to the table.
1751  ********************************************************************/
1752 
1753 gnc_commodity *
1754 gnc_commodity_table_insert(gnc_commodity_table * table,
1755  gnc_commodity * comm)
1756 {
1757  gnc_commodity_namespace * nsp = nullptr;
1758  gnc_commodity *c;
1759  const char *ns_name;
1760  gnc_commodityPrivate* priv;
1761  QofBook *book;
1762 
1763  if (!table) return nullptr;
1764  if (!comm) return nullptr;
1765 
1766  priv = GET_PRIVATE(comm);
1767 
1768  ENTER ("(table=%p, comm=%p) %s %s", table, comm,
1769  (priv->mnemonic == nullptr ? "(null)" : priv->mnemonic),
1770  (priv->fullname == nullptr ? "(null)" : priv->fullname));
1771  ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1772  c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1773 
1774  if (c)
1775  {
1776  if (c == comm)
1777  {
1778  LEAVE("already in table");
1779  return c;
1780  }
1781 
1782  /* Backward compatibility support for currencies that have
1783  * recently changed. */
1784  if (priv->name_space->iso4217)
1785  {
1786  auto it = gnc_new_iso_codes.find (priv->mnemonic);
1787  if (it != gnc_new_iso_codes.end())
1788  gnc_commodity_set_mnemonic(comm, it->second.c_str());
1789  }
1790  gnc_commodity_copy (c, comm);
1791  gnc_commodity_destroy (comm);
1792  LEAVE("found at %p", c);
1793  return c;
1794  }
1795 
1796  /* Prevent setting anything except template in namespace template. */
1797  if (g_strcmp0 (ns_name, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
1798  g_strcmp0 (priv->mnemonic, "template") != 0)
1799  {
1800  PWARN("Converting commodity %s from namespace template to "
1801  "namespace User", priv->mnemonic);
1802  gnc_commodity_set_namespace (comm, "User");
1803  ns_name = "User";
1804  mark_commodity_dirty (comm);
1805  }
1806 
1807  book = qof_instance_get_book (&comm->inst);
1808  nsp = gnc_commodity_table_add_namespace(table, ns_name, book);
1809 
1810  PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic,
1811  nsp->cm_table, nsp->name);
1812  g_hash_table_insert(nsp->cm_table,
1813  (gpointer)CACHE_INSERT(priv->mnemonic),
1814  (gpointer)comm);
1815  nsp->cm_list = g_list_append(nsp->cm_list, comm);
1816 
1817  qof_event_gen (&comm->inst, QOF_EVENT_ADD, nullptr);
1818  LEAVE ("(table=%p, comm=%p)", table, comm);
1819  return comm;
1820 }
1821 
1822 /********************************************************************
1823  * gnc_commodity_table_remove
1824  * remove a commodity from the table.
1825  ********************************************************************/
1826 
1827 void
1828 gnc_commodity_table_remove(gnc_commodity_table * table,
1829  gnc_commodity * comm)
1830 {
1831  gnc_commodity_namespace * nsp;
1832  gnc_commodity *c;
1833  gnc_commodityPrivate* priv;
1834  const char *ns_name;
1835 
1836  if (!table) return;
1837  if (!comm) return;
1838 
1839  priv = GET_PRIVATE(comm);
1840  ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1841  c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1842  if (c != comm) return;
1843 
1844  qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, nullptr);
1845 
1846  nsp = gnc_commodity_table_find_namespace(table, ns_name);
1847  if (!nsp) return;
1848 
1849  nsp->cm_list = g_list_remove(nsp->cm_list, comm);
1850  g_hash_table_remove (nsp->cm_table, priv->mnemonic);
1851  /* XXX minor mem leak, should remove the key as well */
1852 }
1853 
1854 /********************************************************************
1855  * gnc_commodity_table_has_namespace
1856  * see if the commodities namespace exists. May have zero commodities.
1857  ********************************************************************/
1858 
1859 int
1860 gnc_commodity_table_has_namespace(const gnc_commodity_table * table,
1861  const char * name_space)
1862 {
1863  gnc_commodity_namespace * nsp = nullptr;
1864 
1865  if (!table || !name_space)
1866  {
1867  return 0;
1868  }
1869 
1870  nsp = gnc_commodity_table_find_namespace(table, name_space);
1871  if (nsp)
1872  {
1873  return 1;
1874  }
1875  else
1876  {
1877  return 0;
1878  }
1879 }
1880 
1881 static void
1882 hash_keys_helper(gpointer key, gpointer value, gpointer data)
1883 {
1884  auto l = (GList**)data;
1885  *l = g_list_prepend(*l, key);
1886 }
1887 
1888 static GList *
1889 g_hash_table_keys(GHashTable * table)
1890 {
1891  GList * l = nullptr;
1892  g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l);
1893  return l;
1894 }
1895 
1896 static void
1897 hash_values_helper(gpointer key, gpointer value, gpointer data)
1898 {
1899  auto l = (GList**)data;
1900  *l = g_list_prepend(*l, value);
1901 }
1902 
1903 static GList *
1904 g_hash_table_values(GHashTable * table)
1905 {
1906  GList * l = nullptr;
1907  g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l);
1908  return l;
1909 }
1910 
1911 /********************************************************************
1912  * gnc_commodity_table_get_namespaces
1913  * see if any commodities in the namespace exist
1914  ********************************************************************/
1915 
1916 GList *
1917 gnc_commodity_table_get_namespaces(const gnc_commodity_table * table)
1918 {
1919  if (!table)
1920  return nullptr;
1921 
1922  return g_hash_table_keys(table->ns_table);
1923 }
1924 
1925 GList *
1927 {
1928  if (!table)
1929  return nullptr;
1930 
1931  return g_list_copy (table->ns_list);
1932 }
1933 
1934 /* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to
1935  GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is
1936  either of these, the net result is that the iso4217 bit is set only
1937  for GNC_COMMODITY_NS_CURRENCY. This means that gnc_commodity_is_iso is
1938  a subset of gnc_commodity_is_currency. Most callers seem to use
1939  gnc_commodity_is_iso. */
1940 gboolean
1941 gnc_commodity_is_iso(const gnc_commodity * cm)
1942 {
1943  gnc_commodityPrivate* priv;
1944 
1945  if (!cm) return FALSE;
1946 
1947  priv = GET_PRIVATE(cm);
1948  if ( !priv->name_space) return FALSE;
1949  return priv->name_space->iso4217;
1950 }
1951 
1952 gboolean
1953 gnc_commodity_is_currency(const gnc_commodity *cm)
1954 {
1955  const char *ns_name;
1956  if (!cm) return FALSE;
1957 
1958  ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
1959  return (!g_strcmp0(ns_name, GNC_COMMODITY_NS_LEGACY) ||
1960  !g_strcmp0(ns_name, GNC_COMMODITY_NS_CURRENCY));
1961 }
1962 
1963 /********************************************************************
1964  * gnc_commodity_table_get_commodities
1965  * list commodities in a given namespace
1966  ********************************************************************/
1967 
1968 static CommodityList*
1969 commodity_table_get_all_noncurrency_commodities(const gnc_commodity_table* table)
1970 {
1971  GList *node = nullptr, *nslist = gnc_commodity_table_get_namespaces(table);
1972  CommodityList *retval = nullptr;
1973  for (node = nslist; node; node=g_list_next(node))
1974  {
1975  gnc_commodity_namespace *ns = nullptr;
1976  if (g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_CURRENCY) == 0
1977  || g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_TEMPLATE) == 0)
1978  continue;
1979  ns = gnc_commodity_table_find_namespace(table, (char*)(node->data));
1980  if (!ns)
1981  continue;
1982  retval = g_list_concat(g_hash_table_values(ns->cm_table), retval);
1983  }
1984  g_list_free(nslist);
1985  return retval;
1986 }
1987 
1988 CommodityList *
1989 gnc_commodity_table_get_commodities(const gnc_commodity_table * table,
1990  const char * name_space)
1991 {
1992  gnc_commodity_namespace * ns = nullptr;
1993 
1994  if (!table)
1995  return nullptr;
1996  if (g_strcmp0(name_space, GNC_COMMODITY_NS_NONISO_GUI) == 0)
1997  return commodity_table_get_all_noncurrency_commodities(table);
1998  ns = gnc_commodity_table_find_namespace(table, name_space);
1999  if (!ns)
2000  return nullptr;
2001 
2002  return g_hash_table_values(ns->cm_table);
2003 }
2004 
2005 /********************************************************************
2006  * gnc_commodity_table_get_quotable_commodities
2007  * list commodities in a given namespace that get price quotes
2008  ********************************************************************/
2009 
2010 static void
2011 get_quotables_helper1(gpointer key, gpointer value, gpointer data)
2012 {
2013  auto comm = GNC_COMMODITY(value);
2014  gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2015  auto l = static_cast<GList**>(data);
2016 
2017  if (!priv->quote_flag || !priv->quote_source || !priv->quote_source->get_supported())
2018  return;
2019  *l = g_list_prepend(*l, value);
2020 }
2021 
2022 static gboolean
2023 get_quotables_helper2 (gnc_commodity *comm, gpointer data)
2024 {
2025  auto l = static_cast<GList**>(data);
2026  gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2027 
2028  if (!priv->quote_flag || priv->quote_source || !priv->quote_source->get_supported())
2029  return TRUE;
2030  *l = g_list_prepend(*l, comm);
2031  return TRUE;
2032 }
2033 
2034 CommodityList *
2036 {
2037  gnc_commodity_namespace * ns = nullptr;
2038  const char *name_space;
2039  GList * nslist, * tmp;
2040  GList * l = nullptr;
2041  regex_t pattern;
2042  const char *expression = gnc_prefs_get_namespace_regexp();
2043 
2044  ENTER("table=%p, expression=%s", table, expression);
2045  if (!table)
2046  return nullptr;
2047 
2048  if (expression && *expression)
2049  {
2050  if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
2051  {
2052  LEAVE("Cannot compile regex");
2053  return nullptr;
2054  }
2055 
2057  for (tmp = nslist; tmp; tmp = tmp->next)
2058  {
2059  name_space = static_cast<const char*>(tmp->data);
2060  if (regexec(&pattern, name_space, 0, nullptr, 0) == 0)
2061  {
2062  DEBUG("Running list of %s commodities", name_space);
2063  ns = gnc_commodity_table_find_namespace(table, name_space);
2064  if (ns)
2065  {
2066  g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l);
2067  }
2068  }
2069  }
2070  g_list_free(nslist);
2071  regfree(&pattern);
2072  }
2073  else
2074  {
2075  gnc_commodity_table_foreach_commodity(table, get_quotables_helper2,
2076  (gpointer) &l);
2077  }
2078  LEAVE("list head %p", l);
2079  return l;
2080 }
2081 
2082 /********************************************************************
2083  * gnc_commodity_table_add_namespace
2084  * add an empty namespace if it does not exist
2085  ********************************************************************/
2086 
2087 /* GObject Initialization */
2088 QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE)
2089 
2090 static void
2091 gnc_commodity_namespace_init(gnc_commodity_namespace* ns)
2092 {
2093 }
2094 
2095 static void
2096 gnc_commodity_namespace_dispose_real (GObject *nsp)
2097 {
2098 }
2099 
2100 static void
2101 gnc_commodity_namespace_finalize_real(GObject* nsp)
2102 {
2103 }
2104 
2105 gnc_commodity_namespace *
2107  const char * name_space,
2108  QofBook *book)
2109 {
2110  gnc_commodity_namespace * ns = nullptr;
2111 
2112  if (!table) return nullptr;
2113 
2114  name_space = gnc_commodity_table_map_namespace(name_space);
2115  ns = gnc_commodity_table_find_namespace(table, name_space);
2116  if (!ns)
2117  {
2118  ns = static_cast<gnc_commodity_namespace*>(g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, nullptr));
2119  ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal);
2120  ns->name = CACHE_INSERT(static_cast<const char*>(name_space));
2121  ns->iso4217 = gnc_commodity_namespace_is_iso(name_space);
2122  qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book);
2123  qof_event_gen (&ns->inst, QOF_EVENT_CREATE, nullptr);
2124 
2125  g_hash_table_insert(table->ns_table,
2126  (gpointer) ns->name,
2127  (gpointer) ns);
2128  table->ns_list = g_list_append(table->ns_list, ns);
2129  qof_event_gen (&ns->inst, QOF_EVENT_ADD, nullptr);
2130  }
2131  return ns;
2132 }
2133 
2134 bool
2135 gnc_commodity_table_rename_namespace(const gnc_commodity_table * table,
2136  const char * namespace_name,
2137  const char * new_namespace_name)
2138 {
2139  if (!table || !namespace_name || !new_namespace_name ||
2140  (g_strcmp0 (namespace_name, new_namespace_name) == 0) ||
2141  (g_strcmp0 (new_namespace_name, GNC_COMMODITY_NS_ISO_GUI) == 0) ||
2142  (g_strcmp0 (new_namespace_name, _(GNC_COMMODITY_NS_ISO_GUI)) == 0) ||
2143  gnc_commodity_table_find_namespace (table, new_namespace_name))
2144  return false;
2145 
2146  auto ns = gnc_commodity_table_find_namespace (table, namespace_name);
2147  if (!ns)
2148  return false;
2149 
2150  ns->name = CACHE_INSERT(static_cast<const char*>(new_namespace_name));
2151 
2152  g_hash_table_insert (table->ns_table,
2153  (gpointer) ns->name,
2154  (gpointer) ns);
2155 
2156  g_hash_table_remove (table->ns_table,
2157  (gpointer) namespace_name);
2158 
2159  CACHE_REMOVE(namespace_name);
2160 
2161  qof_instance_set_dirty (&ns->inst);
2162  qof_event_gen (&ns->inst, QOF_EVENT_MODIFY, nullptr);
2163  return true;
2164 }
2165 
2166 gnc_commodity_namespace *
2167 gnc_commodity_table_find_namespace(const gnc_commodity_table * table,
2168  const char * name_space)
2169 {
2170  if (!table || !name_space)
2171  return nullptr;
2172 
2173  name_space = gnc_commodity_table_map_namespace(name_space);
2174  return static_cast<gnc_commodity_namespace*>(g_hash_table_lookup(table->ns_table, (gpointer)name_space));
2175 }
2176 
2177 
2178 gnc_commodity *
2179 gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book)
2180 {
2181  QofCollection *col;
2182  if (!guid || !book) return nullptr;
2183  col = qof_book_get_collection (book, GNC_ID_COMMODITY);
2184  return (gnc_commodity *) qof_collection_lookup_entity (col, guid);
2185 }
2186 
2187 /********************************************************************
2188  * gnc_commodity_table_delete_namespace
2189  * delete a namespace
2190  ********************************************************************/
2191 
2192 static int
2193 ns_helper(gpointer key, gpointer value, gpointer user_data)
2194 {
2195  auto c = GNC_COMMODITY(value);
2197  CACHE_REMOVE(static_cast<char*>(key)); /* key is commodity mnemonic */
2198  return TRUE;
2199 }
2200 
2201 void
2203  const char * name_space)
2204 {
2205  gnc_commodity_namespace * ns;
2206 
2207  if (!table) return;
2208 
2209  ns = gnc_commodity_table_find_namespace(table, name_space);
2210  if (!ns)
2211  return;
2212 
2213  qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, nullptr);
2214  g_hash_table_remove(table->ns_table, name_space);
2215  table->ns_list = g_list_remove(table->ns_list, ns);
2216 
2217  g_list_free(ns->cm_list);
2218  ns->cm_list = nullptr;
2219 
2220  g_hash_table_foreach_remove(ns->cm_table, ns_helper, nullptr);
2221  g_hash_table_destroy(ns->cm_table);
2222  CACHE_REMOVE(ns->name);
2223 
2224  qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, nullptr);
2225  /* qof_instance_release(&ns->inst); */
2226  g_object_unref(ns);
2227 }
2228 
2229 /********************************************************************
2230  * gnc_commodity_table_foreach_commodity
2231  * call user-defined function once for every commodity in every
2232  * namespace
2233  ********************************************************************/
2234 
2235 typedef struct
2236 {
2237  gboolean ok;
2238  gboolean (*func)(gnc_commodity *, gpointer);
2239  gpointer user_data;
2240 } IterData;
2241 
2242 static void
2243 iter_commodity (gpointer key, gpointer value, gpointer user_data)
2244 {
2245  IterData *iter_data = (IterData *) user_data;
2246  gnc_commodity *cm = (gnc_commodity *) value;
2247 
2248  if (iter_data->ok)
2249  {
2250  iter_data->ok = (iter_data->func)(cm, iter_data->user_data);
2251  }
2252 }
2253 
2254 static void
2255 iter_namespace (gpointer key, gpointer value, gpointer user_data)
2256 {
2257  GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table;
2258  g_hash_table_foreach (namespace_hash, iter_commodity, user_data);
2259 }
2260 
2261 gboolean
2262 gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl,
2263  gboolean (*f)(gnc_commodity *, gpointer),
2264  gpointer user_data)
2265 {
2266  IterData iter_data;
2267 
2268  if (!tbl || !f) return FALSE;
2269 
2270  iter_data.ok = TRUE;
2271  iter_data.func = f;
2272  iter_data.user_data = user_data;
2273 
2274  g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data);
2275 
2276  return iter_data.ok;
2277 }
2278 
2279 /********************************************************************
2280  * gnc_commodity_table_destroy
2281  * cleanup and free.
2282  ********************************************************************/
2283 
2284 void
2285 gnc_commodity_table_destroy(gnc_commodity_table * t)
2286 {
2287  gnc_commodity_namespace * ns;
2288  GList *item, *next;
2289 
2290  if (!t) return;
2291  ENTER ("table=%p", t);
2292 
2293  for (item = t->ns_list; item; item = next)
2294  {
2295  next = g_list_next(item);
2296  ns = static_cast<gnc_commodity_namespace*>(item->data);
2298  }
2299 
2300  g_list_free(t->ns_list);
2301  t->ns_list = nullptr;
2302  g_hash_table_destroy(t->ns_table);
2303  t->ns_table = nullptr;
2304  LEAVE ("table=%p", t);
2305  g_free(t);
2306 }
2307 
2308 /* =========================================================== */
2309 
2310 /********************************************************************
2311  * gnc_commodity_table_add_default_data
2312  ********************************************************************/
2313 
2314 #define CUR_I18N(String) dgettext ("iso_4217", String)
2315 
2316 gboolean
2318 {
2319  QofCollection *col;
2320  gnc_commodity* c;
2321 
2322  ENTER ("table=%p", table);
2323  gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_TEMPLATE, book);
2324  c = gnc_commodity_new(book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1);
2326 
2327 #include "iso-4217-currencies.c"
2328 
2329  /* We've just created the default namespaces and currencies. Mark
2330  * these collections as clean because there is no USER entered data
2331  * in these collections as of yet. */
2332  col = qof_book_get_collection(book, GNC_ID_COMMODITY);
2334  col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE);
2336 
2337  LEAVE ("table=%p", table);
2338  return TRUE;
2339 }
2340 
2341 /********************************************************************
2342  ********************************************************************/
2343 /* QofObject function implementation and registration */
2344 
2345 #ifdef _MSC_VER
2346 /* MSVC compiler doesn't have C99 "designated initializers"
2347  * so we wrap them in a macro that is empty on MSVC. */
2348 # define DI(x) /* */
2349 #else
2350 # define DI(x) x
2351 #endif
2352 static QofObject commodity_object_def =
2353 {
2354  DI(.interface_version = ) QOF_OBJECT_VERSION,
2355  DI(.e_type = ) GNC_ID_COMMODITY,
2356  DI(.type_label = ) "Commodity",
2357  DI(.create = ) nullptr,
2358  DI(.book_begin = ) nullptr,
2359  DI(.book_end = ) nullptr,
2360  DI(.is_dirty = ) qof_collection_is_dirty,
2361  DI(.mark_clean = ) qof_collection_mark_clean,
2362  DI(.foreach = ) qof_collection_foreach,
2363  DI(.printable = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname,
2364 };
2365 
2366 static QofObject namespace_object_def =
2367 {
2368  DI(.interface_version = ) QOF_OBJECT_VERSION,
2369  DI(.e_type = ) GNC_ID_COMMODITY_NAMESPACE,
2370  DI(.type_label = ) "Namespace",
2371  DI(.create = ) nullptr,
2372  DI(.book_begin = ) nullptr,
2373  DI(.book_end = ) nullptr,
2374  DI(.is_dirty = ) nullptr,
2375  DI(.mark_clean = ) nullptr,
2376  DI(.foreach = ) nullptr,
2377  DI(.printable = ) nullptr,
2378 };
2379 
2380 static void
2381 commodity_table_book_begin (QofBook *book)
2382 {
2383  gnc_commodity_table *ct;
2384  ENTER ("book=%p", book);
2385 
2387  return;
2388 
2389  ct = gnc_commodity_table_new ();
2390  qof_book_set_data (book, GNC_COMMODITY_TABLE, ct);
2391 
2392  if (!gnc_commodity_table_add_default_data(ct, book))
2393  {
2394  PWARN("unable to initialize book's commodity_table");
2395  }
2396 
2397  LEAVE ("book=%p", book);
2398 }
2399 
2400 static void
2401 commodity_table_book_end (QofBook *book)
2402 {
2403  gnc_commodity_table *ct;
2404 
2405  ct = gnc_commodity_table_get_table (book);
2406  qof_book_set_data (book, GNC_COMMODITY_TABLE, nullptr);
2407  gnc_commodity_table_destroy (ct);
2408 }
2409 
2410 static QofObject commodity_table_object_def =
2411 {
2412  DI(.interface_version = ) QOF_OBJECT_VERSION,
2413  DI(.e_type = ) GNC_ID_COMMODITY_TABLE,
2414  DI(.type_label = ) "CommodityTable",
2415  DI(.create = ) nullptr,
2416  DI(.book_begin = ) commodity_table_book_begin,
2417  DI(.book_end = ) commodity_table_book_end,
2418  DI(.is_dirty = ) qof_collection_is_dirty,
2419  DI(.mark_clean = ) qof_collection_mark_clean,
2420  DI(.foreach = ) nullptr,
2421  DI(.printable = ) nullptr,
2422  DI(.version_cmp = ) nullptr,
2423 };
2424 
2425 gboolean
2427 {
2428  if (!qof_object_register (&commodity_object_def))
2429  return FALSE;
2430  if (!qof_object_register (&namespace_object_def))
2431  return FALSE;
2432  return qof_object_register (&commodity_table_object_def);
2433 }
2434 
2435 /* *******************************************************************
2436 * gnc_monetary methods
2437 ********************************************************************/
2438 
2440 MonetaryList *
2441 gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
2442 {
2443  MonetaryList *l = list, *tmp;
2444  for (tmp = list; tmp; tmp = tmp->next)
2445  {
2446  auto list_mon = static_cast<gnc_monetary*>(tmp->data);
2447  if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity))
2448  {
2449  list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
2451  break;
2452  }
2453  }
2454 
2455  /* See if we found an entry, and add one if not */
2456  if (tmp == nullptr)
2457  {
2458  auto new_mon = static_cast<gnc_monetary*>(g_new0(gnc_monetary, 1));
2459  *new_mon = add_mon;
2460  l = g_list_prepend(l, new_mon);
2461  }
2462 
2463  return l;
2464 }
2465 
2468 MonetaryList *
2470 {
2471  MonetaryList *node, *next;
2472  for (node = list; node; node = next)
2473  {
2474  auto mon = static_cast<gnc_monetary*>(node->data);
2475  next = node->next;
2476  if (gnc_numeric_zero_p(mon->value))
2477  {
2478  g_free(mon);
2479  list = g_list_delete_link(list, node);
2480  }
2481  }
2482  return list;
2483 }
2484 
2486 void
2487 gnc_monetary_list_free(MonetaryList *list)
2488 {
2489  MonetaryList *tmp;
2490  for (tmp = list; tmp; tmp = tmp->next)
2491  {
2492  g_free(tmp->data);
2493  }
2494 
2495  g_list_free(list);
2496 }
2497 
2498 /* ========================= 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...
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:232
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:209
#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:188
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:63
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.
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 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:238
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...
bool gnc_commodity_table_rename_namespace(const gnc_commodity_table *table, const char *namespace_name, const char *new_namespace_name)
This function renames a namespace.
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...
QofBook reference.
Definition: qofbook-p.hpp:46
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:245
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...