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