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