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