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