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