|
GnuCash 2.4.99
|
00001 /******************************************************************** 00002 * gnc-commodity.c -- api for tradable commodities (incl. currency) * 00003 * Copyright (C) 2000 Bill Gribble * 00004 * Copyright (C) 2001,2003 Linas Vepstas <linas@linas.org> * 00005 * Copyright (c) 2006 David Hampton <hampton@employees.org> * 00006 * * 00007 * This program is free software; you can redistribute it and/or * 00008 * modify it under the terms of the GNU General Public License as * 00009 * published by the Free Software Foundation; either version 2 of * 00010 * the License, or (at your option) any later version. * 00011 * * 00012 * This program is distributed in the hope that it will be useful, * 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00015 * GNU General Public License for more details. * 00016 * * 00017 * You should have received a copy of the GNU General Public License* 00018 * along with this program; if not, contact: * 00019 * * 00020 * Free Software Foundation Voice: +1-617-542-5942 * 00021 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00022 * Boston, MA 02110-1301, USA gnu@gnu.org * 00023 * * 00024 *******************************************************************/ 00025 00026 #include "config.h" 00027 00028 #include <glib.h> 00029 #include <glib/gi18n.h> 00030 #include <ctype.h> 00031 #include <limits.h> 00032 #include <string.h> 00033 #include <stdio.h> 00034 #include <stdlib.h> 00035 #include <regex.h> 00036 00037 #include "gnc-commodity.h" 00038 #include "gnc-main.h" 00039 00040 static QofLogModule log_module = GNC_MOD_COMMODITY; 00041 00042 /* Parts per unit is nominal, i.e. number of 'partname' units in 00043 * a 'unitname' unit. fraction is transactional, i.e. how many 00044 * of the smallest-transactional-units of the currency are there 00045 * in a 'unitname' unit. */ 00046 00047 enum 00048 { 00049 PROP_0, 00050 PROP_NAMESPACE, 00051 PROP_FULL_NAME, 00052 PROP_MNEMONIC, 00053 PROP_PRINTNAME, 00054 PROP_CUSIP, 00055 PROP_FRACTION, 00056 PROP_UNIQUE_NAME, 00057 PROP_QUOTE_FLAG, 00058 PROP_QUOTE_SOURCE, 00059 PROP_QUOTE_TZ, 00060 }; 00061 00062 struct gnc_commodity_s 00063 { 00064 QofInstance inst; 00065 }; 00066 00067 typedef struct CommodityPrivate 00068 { 00069 gnc_commodity_namespace *namespace; 00070 00071 char * fullname; 00072 char * mnemonic; 00073 char * printname; 00074 char * cusip; /* CUSIP or other identifying code */ 00075 int fraction; 00076 char * unique_name; 00077 00078 gboolean quote_flag; /* user wants price quotes */ 00079 gnc_quote_source * quote_source; /* current/old source of quotes */ 00080 char * quote_tz; 00081 00082 /* the number of accounts using this commodity - this field is not 00083 * persisted */ 00084 int usage_count; 00085 } CommodityPrivate; 00086 00087 #define GET_PRIVATE(o) \ 00088 (G_TYPE_INSTANCE_GET_PRIVATE((o), GNC_TYPE_COMMODITY, CommodityPrivate)) 00089 00090 struct _GncCommodityClass 00091 { 00092 QofInstanceClass parent_class; 00093 }; 00094 00095 static void commodity_free(gnc_commodity * cm); 00096 00097 struct gnc_commodity_namespace_s 00098 { 00099 QofInstance inst; 00100 00101 gchar * name; 00102 gboolean iso4217; 00103 GHashTable * cm_table; 00104 GList * cm_list; 00105 }; 00106 00107 struct _GncCommodityNamespaceClass 00108 { 00109 QofInstanceClass parent_class; 00110 }; 00111 00112 struct gnc_commodity_table_s 00113 { 00114 GHashTable * ns_table; 00115 GList * ns_list; 00116 }; 00117 00118 struct gnc_new_iso_code 00119 { 00120 const char *old_code; 00121 const char *new_code; 00122 } gnc_new_iso_codes[] = 00123 { 00124 {"RUR", "RUB"}, /* Russian Ruble: RUR through 1997-12, RUB from 1998-01 onwards; see bug #393185 */ 00125 {"PLZ", "PLN"}, /* Polish Zloty */ 00126 {"UAG", "UAH"}, /* Ukraine Hryvnia */ 00127 {"NIS", "ILS"}, /* New Israeli Shekel: The informal abbreviation may be "NIS", but 00128 its iso-4217 is clearly ILS and only this! Incorrectly changed 00129 due to bug#152755 (Nov 2004) and changed back again by bug#492417 00130 (Oct 2008). */ 00131 {"MXP", "MXN"}, /* Mexican (Nuevo) Peso */ 00132 {"TRL", "TRY"}, /* New Turkish Lira: changed 2005 */ 00133 00134 /* Only add currencies to this table when the old currency no longer 00135 * exists in the file iso-4217-currencies.scm */ 00136 }; 00137 #define GNC_NEW_ISO_CODES \ 00138 (sizeof(gnc_new_iso_codes) / sizeof(struct gnc_new_iso_code)) 00139 00140 static gboolean fq_is_installed = FALSE; 00141 00142 struct gnc_quote_source_s 00143 { 00144 gboolean supported; 00145 QuoteSourceType type; 00146 gint index; 00147 char *user_name; /* User friendly name */ 00148 char *old_internal_name; /* Name used internally (deprecated) */ 00149 char *internal_name; /* Name used internally and by finance::quote. */ 00150 }; 00151 00152 static gnc_quote_source currency_quote_source = 00153 { TRUE, 0, 0, "Currency", "CURRENCY", "currency" }; 00154 00155 static gnc_quote_source single_quote_sources[] = 00156 { 00157 { FALSE, 0, 0, "Amsterdam Euronext eXchange, NL", "AEX", "aex" }, 00158 { FALSE, 0, 0, "AEX Futures (now in AEX)", "AEX_FUTURES", "aex_futures" }, 00159 { FALSE, 0, 0, "AEX Options (now in AEX)", "AEX_OPTIONS", "aex_options" }, 00160 { FALSE, 0, 0, "American International Assurance, HK", "AIAHK", "aiahk" }, 00161 { FALSE, 0, 0, "Association of Mutual Funds in India", "AMFIINDIA", "amfiindia" }, 00162 { FALSE, 0, 0, "Athens Stock Exchange, GR", "ASEGR", "asegr" }, 00163 { FALSE, 0, 0, "Australian Stock Exchange, AU", "ASX", "asx" }, 00164 { FALSE, 0, 0, "BMO NesbittBurns, CA", "BMONESBITTBURNS", "bmonesbittburns" }, 00165 { FALSE, 0, 0, "BUX/Magyar Tökepiac, HU", "BUX", "bux" }, 00166 { FALSE, 0, 0, "Cominvest, ex-Adig, DE", "COMINVEST", "cominvest" }, 00167 { FALSE, 0, 0, "Deka Investments, DE", "DEKA", "deka" }, 00168 { FALSE, 0, 0, "DWS, DE", "DWS", "dwsfunds" }, 00169 { FALSE, 0, 0, "Fidelity Direct", "FIDELITY_DIRECT", "fidelity_direct" }, 00170 { FALSE, 0, 0, "Finance Canada", "FINANCECANADA", "financecanada" }, 00171 { FALSE, 0, 0, "Finanzpartner, DE", "FINANZPARTNER", "finanzpartner" }, 00172 { FALSE, 0, 0, "First Trust Portfolios, US", "FTPORTFOLIOS_DIRECT", "ftportfolios_direct" }, 00173 { FALSE, 0, 0, "Fund Library, CA", "FUNDLIBRARY", "fundlibrary" }, 00174 { FALSE, 0, 0, "GoldMoney spot rates, JE", "GOLDMONEY", "goldmoney" }, 00175 { FALSE, 0, 0, "HElsinki stock eXchange, FI", "HEX", "hex" }, 00176 { FALSE, 0, 0, "Man Investments, AU", "maninv", "maninv" }, 00177 { FALSE, 0, 0, "Morningstar, SE", "MORNINGSTAR", "morningstar" }, 00178 { FALSE, 0, 0, "Motley Fool, US", "FOOL", "fool" }, 00179 { FALSE, 0, 0, "New Zealand stock eXchange, NZ", "NZX", "nzx" }, 00180 { FALSE, 0, 0, "Paris Stock Exchange/Boursorama, FR", "BOURSO", "bourso" }, 00181 { FALSE, 0, 0, "Paris Stock Exchange/LeRevenu, FR", "LEREVENU", "lerevenu" }, 00182 { FALSE, 0, 0, "Platinum Asset Management, AU", "PLATINUM", "platinum" }, 00183 { FALSE, 0, 0, "Skandinaviska Enskilda Banken, SE", "SEB_FUNDS", "seb_funds" }, 00184 { FALSE, 0, 0, "Sharenet, ZA", "ZA", "za" }, 00185 { FALSE, 0, 0, "StockHouse Canada", "STOCKHOUSE_FUND", "stockhousecanada_fund" }, 00186 { FALSE, 0, 0, "TD Waterhouse Canada", "TDWATERHOUSE", "tdwaterhouse" }, 00187 { FALSE, 0, 0, "TD Efunds, CA", "TDEFUNDS", "tdefunds" }, 00188 { FALSE, 0, 0, "TIAA-CREF, US", "TIAACREF", "tiaacref" }, 00189 { FALSE, 0, 0, "Toronto Stock eXchange, CA", "TSX", "tsx" }, 00190 { FALSE, 0, 0, "T. Rowe Price, US", "TRPRICE_DIRECT", "troweprice_direct" }, 00191 { FALSE, 0, 0, "Trustnet, GB", "TRUSTNET", "trustnet" }, 00192 { FALSE, 0, 0, "Union Investment, DE", "UNIONFUNDS", "unionfunds" }, 00193 { FALSE, 0, 0, "US Treasury Bonds", "usfedbonds", "usfedbonds" }, 00194 { FALSE, 0, 0, "US Govt. Thrift Savings Plan", "TSP", "tsp" }, 00195 { FALSE, 0, 0, "Vanguard", "VANGUARD", "vanguard" }, /* No module seen in F::Q 1.17. */ 00196 { FALSE, 0, 0, "VWD, DE (unmaintained)", "VWD", "vwd" }, 00197 { FALSE, 0, 0, "Yahoo USA", "YAHOO", "yahoo" }, 00198 { FALSE, 0, 0, "Yahoo Asia", "YAHOO_ASIA", "yahoo_asia" }, 00199 { FALSE, 0, 0, "Yahoo Australia", "YAHOO_AUSTRALIA", "yahoo_australia" }, 00200 { FALSE, 0, 0, "Yahoo Brasil", "YAHOO_BRASIL", "yahoo_brasil" }, 00201 { FALSE, 0, 0, "Yahoo Europe", "YAHOO_EUROPE", "yahoo_europe" }, 00202 { FALSE, 0, 0, "Yahoo New Zealand", "YAHOO_NZ", "yahoo_nz" }, 00203 { FALSE, 0, 0, "Zuerich Investments (outdated)", "ZIFUNDS", "zifunds" }, /* Removed from F::Q 1.11. */ 00204 }; 00205 static gnc_quote_source multiple_quote_sources[] = 00206 { 00207 { FALSE, 0, 0, "Asia (Yahoo, ...)", "ASIA", "asia" }, 00208 { FALSE, 0, 0, "Australia (ASX, Yahoo, ...)", "AUSTRALIA", "australia" }, 00209 { FALSE, 0, 0, "Brasil (Yahoo, ...)", "BRASIL", "brasil" }, 00210 { FALSE, 0, 0, "Canada (Yahoo, ...)", "CANADA", "canada" }, 00211 { FALSE, 0, 0, "Canada Mutual (Fund Library, ...)", "CANADAMUTUAL", "canadamutual" }, 00212 { FALSE, 0, 0, "Dutch (AEX, ...)", "DUTCH", "dutch" }, 00213 { FALSE, 0, 0, "Europe (Yahoo, ...)", "EUROPE", "europe" }, 00214 { FALSE, 0, 0, "Greece (ASE, ...)", "GREECE", "greece" }, 00215 { FALSE, 0, 0, "India Mutual (AMFI, ...)", "INDIAMUTUAL", "indiamutual" }, 00216 { FALSE, 0, 0, "Fidelity (Fidelity, ...)", "FIDELITY", "fidelity" }, 00217 { FALSE, 0, 0, "Finland (HEX, ...)", "FINLAND", "finland" }, 00218 { FALSE, 0, 0, "First Trust (First Trust, ...)", "FTPORTFOLIOS", "ftportfolios" }, 00219 { FALSE, 0, 0, "France (Boursorama, ...)", "FRANCE", "france" }, 00220 { FALSE, 0, 0, "Nasdaq (Yahoo, ...)", "NASDAQ", "nasdaq" }, 00221 { FALSE, 0, 0, "New Zealand (Yahoo, ...)", "NZ", "nz" }, 00222 { FALSE, 0, 0, "NYSE (Yahoo, ...)", "NYSE", "nyse" }, 00223 /* { FALSE, 0, 0, "South Africa (Sharenet, ...)", "ZA", "za" }, */ 00224 { FALSE, 0, 0, "T. Rowe Price", "TRPRICE", "troweprice" }, 00225 { FALSE, 0, 0, "U.K. Unit Trusts", "UKUNITTRUSTS", "uk_unit_trusts" }, 00226 { FALSE, 0, 0, "USA (Yahoo, Fool ...)", "USA", "usa" }, 00227 }; 00228 00229 static const int num_single_quote_sources = 00230 sizeof(single_quote_sources) / sizeof(gnc_quote_source); 00231 static const int num_multiple_quote_sources = 00232 sizeof(multiple_quote_sources) / sizeof(gnc_quote_source); 00233 static GList *new_quote_sources = NULL; 00234 00235 00236 /******************************************************************** 00237 * gnc_quote_source_fq_installed 00238 * 00239 * This function indicates whether or not the Finance::Quote module 00240 * is installed on a users computer. 00241 ********************************************************************/ 00242 gboolean 00243 gnc_quote_source_fq_installed (void) 00244 { 00245 return fq_is_installed; 00246 } 00247 00248 /******************************************************************** 00249 * gnc_quote_source_num_entries 00250 * 00251 * Return the number of entries for a given type of price source. 00252 ********************************************************************/ 00253 gint gnc_quote_source_num_entries(QuoteSourceType type) 00254 { 00255 if (type == SOURCE_CURRENCY) 00256 return 1; 00257 00258 if (type == SOURCE_SINGLE) 00259 return num_single_quote_sources; 00260 00261 if (type == SOURCE_MULTI) 00262 return num_multiple_quote_sources; 00263 00264 return g_list_length(new_quote_sources); 00265 } 00266 00267 /******************************************************************** 00268 * gnc_quote_source_init_tables 00269 * 00270 * Update the type/index values for prices sources. 00271 ********************************************************************/ 00272 static void 00273 gnc_quote_source_init_tables (void) 00274 { 00275 gint i; 00276 00277 for (i = 0; i < num_single_quote_sources; i++) 00278 { 00279 single_quote_sources[i].type = SOURCE_SINGLE; 00280 single_quote_sources[i].index = i; 00281 } 00282 00283 for (i = 0; i < num_multiple_quote_sources; i++) 00284 { 00285 multiple_quote_sources[i].type = SOURCE_MULTI; 00286 multiple_quote_sources[i].index = i; 00287 } 00288 00289 currency_quote_source.type = SOURCE_CURRENCY; 00290 currency_quote_source.index = 0; 00291 } 00292 00293 00294 /******************************************************************** 00295 * gnc_quote_source_add_new 00296 * 00297 * Add a new price source. Called when unknown source names are found 00298 * either in the F::Q installation (a newly available source) or in 00299 * the user's data file (a source that has vanished but needs to be 00300 * tracked.) 00301 ********************************************************************/ 00302 gnc_quote_source * 00303 gnc_quote_source_add_new (const char *source_name, gboolean supported) 00304 { 00305 gnc_quote_source *new_source; 00306 00307 DEBUG("Creating new source %s", (source_name == NULL ? "(null)" : source_name)); 00308 new_source = malloc(sizeof(gnc_quote_source)); 00309 new_source->supported = supported; 00310 new_source->type = SOURCE_UNKNOWN; 00311 new_source->index = g_list_length(new_quote_sources); 00312 00313 /* This name can be changed if/when support for this price source is 00314 * integrated into gnucash. */ 00315 new_source->user_name = g_strdup(source_name); 00316 00317 /* This name is permanent and must be kept the same if/when support 00318 * for this price source is integrated into gnucash (i.e. for a 00319 * nice user name). */ 00320 new_source->old_internal_name = g_strdup(source_name); 00321 new_source->internal_name = g_strdup(source_name); 00322 new_quote_sources = g_list_append(new_quote_sources, new_source); 00323 return new_source; 00324 } 00325 00326 /******************************************************************** 00327 * gnc_quote_source_lookup_by_xxx 00328 * 00329 * Lookup a price source data structure based upon various criteria. 00330 ********************************************************************/ 00331 gnc_quote_source * 00332 gnc_quote_source_lookup_by_ti (QuoteSourceType type, gint index) 00333 { 00334 gnc_quote_source *source; 00335 GList *node; 00336 00337 ENTER("type/index is %d/%d", type, index); 00338 switch (type) 00339 { 00340 case SOURCE_CURRENCY: 00341 LEAVE("found %s", currency_quote_source.user_name); 00342 return ¤cy_quote_source; 00343 break; 00344 00345 case SOURCE_SINGLE: 00346 if (index < num_single_quote_sources) 00347 { 00348 LEAVE("found %s", single_quote_sources[index].user_name); 00349 return &single_quote_sources[index]; 00350 } 00351 break; 00352 00353 case SOURCE_MULTI: 00354 if (index < num_multiple_quote_sources) 00355 { 00356 LEAVE("found %s", multiple_quote_sources[index].user_name); 00357 return &multiple_quote_sources[index]; 00358 } 00359 break; 00360 00361 case SOURCE_UNKNOWN: 00362 default: 00363 node = g_list_nth(new_quote_sources, index); 00364 if (node) 00365 { 00366 source = node->data; 00367 LEAVE("found %s", source->user_name); 00368 return source; 00369 } 00370 } 00371 00372 LEAVE("not found"); 00373 return NULL; 00374 } 00375 00376 gnc_quote_source * 00377 gnc_quote_source_lookup_by_internal(const char * name) 00378 { 00379 gnc_quote_source *source; 00380 GList *node; 00381 gint i; 00382 00383 if ((name == NULL) || (safe_strcmp(name, "") == 0)) 00384 { 00385 return NULL; 00386 } 00387 00388 if (safe_strcmp(name, currency_quote_source.internal_name) == 0) 00389 return ¤cy_quote_source; 00390 if (safe_strcmp(name, currency_quote_source.old_internal_name) == 0) 00391 return ¤cy_quote_source; 00392 00393 for (i = 0; i < num_single_quote_sources; i++) 00394 { 00395 if (safe_strcmp(name, single_quote_sources[i].internal_name) == 0) 00396 return &single_quote_sources[i]; 00397 if (safe_strcmp(name, single_quote_sources[i].old_internal_name) == 0) 00398 return &single_quote_sources[i]; 00399 } 00400 00401 for (i = 0; i < num_multiple_quote_sources; i++) 00402 { 00403 if (safe_strcmp(name, multiple_quote_sources[i].internal_name) == 0) 00404 return &multiple_quote_sources[i]; 00405 if (safe_strcmp(name, multiple_quote_sources[i].old_internal_name) == 0) 00406 return &multiple_quote_sources[i]; 00407 } 00408 00409 for (i = 0, node = new_quote_sources; node; node = node->next, i++) 00410 { 00411 source = node->data; 00412 if (safe_strcmp(name, source->internal_name) == 0) 00413 return source; 00414 if (safe_strcmp(name, source->old_internal_name) == 0) 00415 return source; 00416 } 00417 00418 DEBUG("gnc_quote_source_lookup_by_internal: Unknown source %s", name); 00419 return NULL; 00420 } 00421 00422 /******************************************************************** 00423 * gnc_quote_source_get_xxx 00424 * 00425 * Accessor functions - get functions only. There are no set functions. 00426 ********************************************************************/ 00427 QuoteSourceType 00428 gnc_quote_source_get_type (const gnc_quote_source *source) 00429 { 00430 ENTER("%p", source); 00431 if (!source) 00432 { 00433 LEAVE("bad source"); 00434 return SOURCE_SINGLE; 00435 } 00436 00437 LEAVE("type is %d", source->type); 00438 return source->type; 00439 } 00440 00441 gint 00442 gnc_quote_source_get_index (const gnc_quote_source *source) 00443 { 00444 ENTER("%p", source); 00445 if (!source) 00446 { 00447 LEAVE("bad source"); 00448 return 0; 00449 } 00450 00451 LEAVE("index is %d", source->index); 00452 return source->index; 00453 } 00454 00455 gboolean 00456 gnc_quote_source_get_supported (const gnc_quote_source *source) 00457 { 00458 ENTER("%p", source); 00459 if (!source) 00460 { 00461 LEAVE("bad source"); 00462 return FALSE; 00463 } 00464 00465 LEAVE("%ssupported", source && source->supported ? "" : "not "); 00466 return source->supported; 00467 } 00468 00469 const char * 00470 gnc_quote_source_get_user_name (const gnc_quote_source *source) 00471 { 00472 ENTER("%p", source); 00473 if (!source) 00474 { 00475 LEAVE("bad source"); 00476 return NULL; 00477 } 00478 LEAVE("user name %s", source->user_name); 00479 return source->user_name; 00480 } 00481 00482 const char * 00483 gnc_quote_source_get_internal_name (const gnc_quote_source *source) 00484 { 00485 ENTER("%p", source); 00486 if (!source) 00487 { 00488 LEAVE("bad source"); 00489 return NULL; 00490 } 00491 LEAVE("internal name %s", source->internal_name); 00492 return source->internal_name; 00493 } 00494 00495 /******************************************************************** 00496 * gnc_quote_source_set_fq_installed 00497 * 00498 * Update gnucash internal tables on what Finance::Quote sources are 00499 * installed. 00500 ********************************************************************/ 00501 void 00502 gnc_quote_source_set_fq_installed (const GList *sources_list) 00503 { 00504 gnc_quote_source *source; 00505 char *source_name; 00506 const GList *node; 00507 00508 ENTER(" "); 00509 fq_is_installed = TRUE; 00510 00511 if (!sources_list) 00512 return; 00513 00514 for (node = sources_list; node; node = node->next) 00515 { 00516 source_name = node->data; 00517 00518 source = gnc_quote_source_lookup_by_internal(source_name); 00519 if (source != NULL) 00520 { 00521 DEBUG("Found source %s: %s", source_name, source->user_name); 00522 source->supported = TRUE; 00523 continue; 00524 } 00525 00526 gnc_quote_source_add_new(source_name, TRUE); 00527 } 00528 LEAVE(" "); 00529 } 00530 00531 /******************************************************************** 00532 * QoF Helpers 00533 ********************************************************************/ 00534 00535 void 00536 gnc_commodity_begin_edit (gnc_commodity *cm) 00537 { 00538 qof_begin_edit(&cm->inst); 00539 } 00540 00541 static void commit_err (QofInstance *inst, QofBackendError errcode) 00542 { 00543 PERR ("Failed to commit: %d", errcode); 00544 gnc_engine_signal_commit_error( errcode ); 00545 } 00546 00547 static void noop (QofInstance *inst) {} 00548 00549 static void 00550 comm_free(QofInstance* inst) 00551 { 00552 commodity_free( GNC_COMMODITY(inst) ); 00553 } 00554 00555 void 00556 gnc_commodity_commit_edit (gnc_commodity *cm) 00557 { 00558 if (!qof_commit_edit (QOF_INSTANCE(cm))) return; 00559 qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free); 00560 } 00561 00562 /******************************************************************** 00563 * gnc_commodity_new 00564 ********************************************************************/ 00565 00566 static void 00567 mark_commodity_dirty (gnc_commodity *cm) 00568 { 00569 qof_instance_set_dirty(&cm->inst); 00570 qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, NULL); 00571 } 00572 00573 static void 00574 reset_printname(CommodityPrivate *priv) 00575 { 00576 g_free(priv->printname); 00577 priv->printname = g_strdup_printf("%s (%s)", 00578 priv->mnemonic ? priv->mnemonic : "", 00579 priv->fullname ? priv->fullname : ""); 00580 } 00581 00582 static void 00583 reset_unique_name(CommodityPrivate *priv) 00584 { 00585 gnc_commodity_namespace *ns; 00586 00587 g_free(priv->unique_name); 00588 ns = priv->namespace; 00589 priv->unique_name = g_strdup_printf("%s::%s", 00590 ns ? ns->name : "", 00591 priv->mnemonic ? priv->mnemonic : ""); 00592 } 00593 00594 /* GObject Initialization */ 00595 G_DEFINE_TYPE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE); 00596 00597 static void 00598 gnc_commodity_init(gnc_commodity* com) 00599 { 00600 CommodityPrivate* priv; 00601 00602 priv = GET_PRIVATE(com); 00603 00604 priv->namespace = NULL; 00605 priv->fullname = CACHE_INSERT(""); 00606 priv->mnemonic = CACHE_INSERT(""); 00607 priv->cusip = CACHE_INSERT(""); 00608 priv->fraction = 10000; 00609 priv->quote_flag = 0; 00610 priv->quote_source = NULL; 00611 priv->quote_tz = CACHE_INSERT(""); 00612 00613 reset_printname(priv); 00614 reset_unique_name(priv); 00615 } 00616 00617 static void 00618 gnc_commodity_dispose(GObject *comp) 00619 { 00620 G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp); 00621 } 00622 00623 static void 00624 gnc_commodity_finalize(GObject* comp) 00625 { 00626 G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp); 00627 } 00628 00629 static void 00630 gnc_commodity_get_property (GObject *object, 00631 guint prop_id, 00632 GValue *value, 00633 GParamSpec *pspec) 00634 { 00635 gnc_commodity *commodity; 00636 CommodityPrivate* priv; 00637 00638 g_return_if_fail(GNC_IS_COMMODITY(object)); 00639 00640 commodity = GNC_COMMODITY(object); 00641 priv = GET_PRIVATE(commodity); 00642 switch (prop_id) 00643 { 00644 case PROP_NAMESPACE: 00645 g_value_set_object(value, priv->namespace); 00646 break; 00647 case PROP_FULL_NAME: 00648 g_value_set_string(value, priv->fullname); 00649 break; 00650 case PROP_MNEMONIC: 00651 g_value_set_string(value, priv->mnemonic); 00652 break; 00653 case PROP_PRINTNAME: 00654 g_value_set_string(value, priv->printname); 00655 break; 00656 case PROP_CUSIP: 00657 g_value_set_string(value, priv->cusip); 00658 break; 00659 case PROP_FRACTION: 00660 g_value_set_int(value, priv->fraction); 00661 break; 00662 case PROP_UNIQUE_NAME: 00663 g_value_set_string(value, priv->unique_name); 00664 break; 00665 case PROP_QUOTE_FLAG: 00666 g_value_set_boolean(value, priv->quote_flag); 00667 break; 00668 case PROP_QUOTE_SOURCE: 00669 g_value_set_pointer(value, priv->quote_source); 00670 break; 00671 case PROP_QUOTE_TZ: 00672 g_value_set_string(value, priv->quote_tz); 00673 break; 00674 default: 00675 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00676 break; 00677 } 00678 } 00679 00680 static void 00681 gnc_commodity_set_property (GObject *object, 00682 guint prop_id, 00683 const GValue *value, 00684 GParamSpec *pspec) 00685 { 00686 gnc_commodity *commodity; 00687 00688 g_return_if_fail(GNC_IS_COMMODITY(object)); 00689 00690 commodity = GNC_COMMODITY(object); 00691 00692 switch (prop_id) 00693 { 00694 case PROP_NAMESPACE: 00695 gnc_commodity_set_namespace(commodity, g_value_get_object(value)); 00696 break; 00697 case PROP_FULL_NAME: 00698 gnc_commodity_set_fullname(commodity, g_value_get_string(value)); 00699 break; 00700 case PROP_MNEMONIC: 00701 gnc_commodity_set_mnemonic(commodity, g_value_get_string(value)); 00702 break; 00703 case PROP_CUSIP: 00704 gnc_commodity_set_cusip(commodity, g_value_get_string(value)); 00705 break; 00706 case PROP_FRACTION: 00707 gnc_commodity_set_fraction(commodity, g_value_get_int(value)); 00708 break; 00709 case PROP_QUOTE_FLAG: 00710 gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value)); 00711 break; 00712 case PROP_QUOTE_SOURCE: 00713 gnc_commodity_set_quote_source(commodity, g_value_get_pointer(value)); 00714 break; 00715 case PROP_QUOTE_TZ: 00716 gnc_commodity_set_quote_tz(commodity, g_value_get_string(value)); 00717 break; 00718 default: 00719 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00720 break; 00721 } 00722 } 00723 static void 00724 gnc_commodity_class_init(struct _GncCommodityClass* klass) 00725 { 00726 GObjectClass *gobject_class = G_OBJECT_CLASS(klass); 00727 00728 gobject_class->dispose = gnc_commodity_dispose; 00729 gobject_class->finalize = gnc_commodity_finalize; 00730 gobject_class->set_property = gnc_commodity_set_property; 00731 gobject_class->get_property = gnc_commodity_get_property; 00732 00733 g_type_class_add_private(klass, sizeof(CommodityPrivate)); 00734 00735 g_object_class_install_property(gobject_class, 00736 PROP_NAMESPACE, 00737 g_param_spec_object ("namespace", 00738 "Namespace", 00739 "The namespace field denotes the " 00740 "namespace for this commodity, either " 00741 "a currency or symbol from a quote source.", 00742 GNC_TYPE_COMMODITY_NAMESPACE, 00743 G_PARAM_READWRITE)); 00744 g_object_class_install_property(gobject_class, 00745 PROP_FULL_NAME, 00746 g_param_spec_string ("fullname", 00747 "Full Commodity Name", 00748 "The fullname is the official full name of" 00749 "the currency.", 00750 NULL, 00751 G_PARAM_READWRITE)); 00752 g_object_class_install_property(gobject_class, 00753 PROP_MNEMONIC, 00754 g_param_spec_string ("mnemonic", 00755 "Commodity Mnemonic", 00756 "The mnemonic is the official abbreviated" 00757 "designation for the currency.", 00758 NULL, 00759 G_PARAM_READWRITE)); 00760 g_object_class_install_property(gobject_class, 00761 PROP_PRINTNAME, 00762 g_param_spec_string ("printname", 00763 "Commodity Print Name", 00764 "Printable form of the commodity name.", 00765 NULL, 00766 G_PARAM_READABLE)); 00767 g_object_class_install_property(gobject_class, 00768 PROP_CUSIP, 00769 g_param_spec_string ("cusip", 00770 "Commodity CUSIP Code", 00771 "?????", 00772 NULL, 00773 G_PARAM_READWRITE)); 00774 g_object_class_install_property(gobject_class, 00775 PROP_FRACTION, 00776 g_param_spec_int ("fraction", 00777 "Fraction", 00778 "The fraction is the number of sub-units that " 00779 "the basic commodity can be divided into.", 00780 1, 00781 1000000, 00782 1, 00783 G_PARAM_READWRITE)); 00784 g_object_class_install_property(gobject_class, 00785 PROP_UNIQUE_NAME, 00786 g_param_spec_string ("unique-name", 00787 "Commodity Unique Name", 00788 "Unique form of the commodity name which combines " 00789 "the namespace name and the commodity name.", 00790 NULL, 00791 G_PARAM_READABLE)); 00792 g_object_class_install_property(gobject_class, 00793 PROP_QUOTE_FLAG, 00794 g_param_spec_boolean ("quote_flag", 00795 "Quote Flag", 00796 "TRUE if prices are to be downloaded for this " 00797 "commodity from a quote source.", 00798 FALSE, 00799 G_PARAM_READWRITE)); 00800 g_object_class_install_property(gobject_class, 00801 PROP_QUOTE_SOURCE, 00802 g_param_spec_pointer("quote-source", 00803 "Quote Source", 00804 "The quote source from which prices are downloaded.", 00805 G_PARAM_READWRITE)); 00806 g_object_class_install_property(gobject_class, 00807 PROP_QUOTE_TZ, 00808 g_param_spec_string ("quote-tz", 00809 "Commodity Quote Timezone", 00810 "?????", 00811 NULL, 00812 G_PARAM_READWRITE)); 00813 } 00814 00815 gnc_commodity * 00816 gnc_commodity_new(QofBook *book, const char * fullname, 00817 const char * namespace, const char * mnemonic, 00818 const char * cusip, int fraction) 00819 { 00820 gnc_commodity * retval = g_object_new(GNC_TYPE_COMMODITY, NULL); 00821 00822 qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book); 00823 gnc_commodity_begin_edit(retval); 00824 00825 if ( namespace != NULL ) 00826 { 00827 gnc_commodity_set_namespace(retval, namespace); 00828 if (gnc_commodity_namespace_is_iso(namespace)) 00829 { 00830 gnc_commodity_set_quote_source(retval, 00831 gnc_quote_source_lookup_by_internal("currency") ); 00832 } 00833 } 00834 gnc_commodity_set_fullname(retval, fullname); 00835 gnc_commodity_set_mnemonic(retval, mnemonic); 00836 gnc_commodity_set_cusip(retval, cusip); 00837 gnc_commodity_set_fraction(retval, fraction); 00838 gnc_commodity_commit_edit(retval); 00839 00840 qof_event_gen (&retval->inst, QOF_EVENT_CREATE, NULL); 00841 00842 return retval; 00843 } 00844 00845 00846 /******************************************************************** 00847 * gnc_commodity_destroy 00848 ********************************************************************/ 00849 00850 static void 00851 commodity_free(gnc_commodity * cm) 00852 { 00853 QofBook *book; 00854 gnc_commodity_table *table; 00855 CommodityPrivate* priv; 00856 00857 if (!cm) return; 00858 00859 book = qof_instance_get_book(&cm->inst); 00860 table = gnc_commodity_table_get_table(book); 00861 gnc_commodity_table_remove(table, cm); 00862 priv = GET_PRIVATE(cm); 00863 00864 qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, NULL); 00865 00866 /* Set at creation */ 00867 CACHE_REMOVE (priv->fullname); 00868 CACHE_REMOVE (priv->cusip); 00869 CACHE_REMOVE (priv->mnemonic); 00870 CACHE_REMOVE (priv->quote_tz); 00871 priv->namespace = NULL; 00872 00873 /* Set through accessor functions */ 00874 priv->quote_source = NULL; 00875 00876 /* Automatically generated */ 00877 g_free(priv->printname); 00878 priv->printname = NULL; 00879 00880 g_free(priv->unique_name); 00881 priv->unique_name = NULL; 00882 00883 #ifdef ACCOUNTS_CLEANED_UP 00884 /* Account objects are not actually cleaned up when a book is closed (in fact 00885 * a memory leak), but commodities are, so in currently this warning gets hit 00886 * quite frequently. Disable the check until cleaning up of accounts objects 00887 * on close is implemented. */ 00888 if (priv->usage_count != 0) 00889 { 00890 PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm, 00891 priv->usage_count); 00892 } 00893 #endif 00894 00895 /* qof_instance_release (&cm->inst); */ 00896 g_object_unref(cm); 00897 } 00898 00899 void 00900 gnc_commodity_destroy(gnc_commodity * cm) 00901 { 00902 gnc_commodity_begin_edit(cm); 00903 qof_instance_set_destroying(cm, TRUE); 00904 gnc_commodity_commit_edit(cm); 00905 } 00906 00907 void 00908 gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src) 00909 { 00910 CommodityPrivate* src_priv = GET_PRIVATE(src); 00911 CommodityPrivate* dest_priv = GET_PRIVATE(dest); 00912 00913 gnc_commodity_set_fullname (dest, src_priv->fullname); 00914 gnc_commodity_set_mnemonic (dest, src_priv->mnemonic); 00915 dest_priv->namespace = src_priv->namespace; 00916 gnc_commodity_set_fraction (dest, src_priv->fraction); 00917 gnc_commodity_set_cusip (dest, src_priv->cusip); 00918 gnc_commodity_set_quote_flag (dest, src_priv->quote_flag); 00919 gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src)); 00920 gnc_commodity_set_quote_tz (dest, src_priv->quote_tz); 00921 kvp_frame_delete (dest->inst.kvp_data); 00922 dest->inst.kvp_data = kvp_frame_copy (src->inst.kvp_data); 00923 kvp_frame_delete (dest->inst.kvp_data); 00924 dest->inst.kvp_data = kvp_frame_copy (src->inst.kvp_data); 00925 } 00926 00927 gnc_commodity * 00928 gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book) 00929 { 00930 CommodityPrivate* src_priv; 00931 CommodityPrivate* dest_priv; 00932 00933 gnc_commodity * dest = g_object_new(GNC_TYPE_COMMODITY, NULL); 00934 qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book); 00935 src_priv = GET_PRIVATE(src); 00936 dest_priv = GET_PRIVATE(dest); 00937 00938 dest_priv->fullname = CACHE_INSERT(src_priv->fullname); 00939 dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic); 00940 dest_priv->cusip = CACHE_INSERT(src_priv->cusip); 00941 dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz); 00942 00943 dest_priv->namespace = src_priv->namespace; 00944 00945 dest_priv->fraction = src_priv->fraction; 00946 dest_priv->quote_flag = src_priv->quote_flag; 00947 00948 gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src)); 00949 00950 kvp_frame_delete (dest->inst.kvp_data); 00951 dest->inst.kvp_data = kvp_frame_copy (src->inst.kvp_data); 00952 00953 reset_printname(dest_priv); 00954 reset_unique_name(dest_priv); 00955 00956 return dest; 00957 } 00958 00959 /******************************************************************** 00960 * gnc_commodity_get_mnemonic 00961 ********************************************************************/ 00962 00963 const char * 00964 gnc_commodity_get_mnemonic(const gnc_commodity * cm) 00965 { 00966 if (!cm) return NULL; 00967 return GET_PRIVATE(cm)->mnemonic; 00968 } 00969 00970 /******************************************************************** 00971 * gnc_commodity_get_printname 00972 ********************************************************************/ 00973 00974 const char * 00975 gnc_commodity_get_printname(const gnc_commodity * cm) 00976 { 00977 if (!cm) return NULL; 00978 return GET_PRIVATE(cm)->printname; 00979 } 00980 00981 00982 /******************************************************************** 00983 * gnc_commodity_get_namespace 00984 ********************************************************************/ 00985 00986 const char * 00987 gnc_commodity_get_namespace(const gnc_commodity * cm) 00988 { 00989 if (!cm) return NULL; 00990 return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->namespace); 00991 } 00992 00993 const char * 00994 gnc_commodity_get_namespace_compat(const gnc_commodity * cm) 00995 { 00996 CommodityPrivate* priv; 00997 00998 if (!cm) return NULL; 00999 priv = GET_PRIVATE(cm); 01000 if (!priv->namespace) return NULL; 01001 if (priv->namespace->iso4217) 01002 { 01003 /* Data files are still written with ISO4217. */ 01004 return GNC_COMMODITY_NS_ISO; 01005 } 01006 return gnc_commodity_namespace_get_name(priv->namespace); 01007 } 01008 01009 gnc_commodity_namespace * 01010 gnc_commodity_get_namespace_ds(const gnc_commodity * cm) 01011 { 01012 if (!cm) return NULL; 01013 return GET_PRIVATE(cm)->namespace; 01014 } 01015 01016 /******************************************************************** 01017 * gnc_commodity_get_fullname 01018 ********************************************************************/ 01019 01020 const char * 01021 gnc_commodity_get_fullname(const gnc_commodity * cm) 01022 { 01023 if (!cm) return NULL; 01024 return GET_PRIVATE(cm)->fullname; 01025 } 01026 01027 01028 /******************************************************************** 01029 * gnc_commodity_get_unique_name 01030 ********************************************************************/ 01031 01032 const char * 01033 gnc_commodity_get_unique_name(const gnc_commodity * cm) 01034 { 01035 if (!cm) return NULL; 01036 return GET_PRIVATE(cm)->unique_name; 01037 } 01038 01039 01040 /******************************************************************** 01041 * gnc_commodity_get_cusip 01042 ********************************************************************/ 01043 01044 const char * 01045 gnc_commodity_get_cusip(const gnc_commodity * cm) 01046 { 01047 if (!cm) return NULL; 01048 return GET_PRIVATE(cm)->cusip; 01049 } 01050 01051 /******************************************************************** 01052 * gnc_commodity_get_fraction 01053 ********************************************************************/ 01054 01055 int 01056 gnc_commodity_get_fraction(const gnc_commodity * cm) 01057 { 01058 if (!cm) return 0; 01059 return GET_PRIVATE(cm)->fraction; 01060 } 01061 01062 /******************************************************************** 01063 * gnc_commodity_get_auto_quote_control_flag 01064 ********************************************************************/ 01065 01066 static gboolean 01067 gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm) 01068 { 01069 const char *str; 01070 01071 if (!cm) return FALSE; 01072 01073 str = kvp_frame_get_string(cm->inst.kvp_data, "auto_quote_control"); 01074 return !str || (strcmp(str, "false") != 0); 01075 } 01076 01077 /******************************************************************** 01078 * gnc_commodity_get_quote_flag 01079 ********************************************************************/ 01080 01081 gboolean 01082 gnc_commodity_get_quote_flag(const gnc_commodity *cm) 01083 { 01084 if (!cm) return FALSE; 01085 return (GET_PRIVATE(cm)->quote_flag); 01086 } 01087 01088 /******************************************************************** 01089 * gnc_commodity_get_quote_source 01090 ********************************************************************/ 01091 01092 gnc_quote_source* 01093 gnc_commodity_get_quote_source(const gnc_commodity *cm) 01094 { 01095 CommodityPrivate* priv; 01096 01097 if (!cm) return NULL; 01098 priv = GET_PRIVATE(cm); 01099 if (!priv->quote_source && gnc_commodity_is_iso(cm)) 01100 return ¤cy_quote_source; 01101 return priv->quote_source; 01102 } 01103 01104 gnc_quote_source* 01105 gnc_commodity_get_default_quote_source(const gnc_commodity *cm) 01106 { 01107 if (cm && gnc_commodity_is_iso(cm)) 01108 return ¤cy_quote_source; 01109 /* Should make this a user option at some point. */ 01110 return gnc_quote_source_lookup_by_internal("yahoo"); 01111 } 01112 01113 /******************************************************************** 01114 * gnc_commodity_get_quote_tz 01115 ********************************************************************/ 01116 01117 const char* 01118 gnc_commodity_get_quote_tz(const gnc_commodity *cm) 01119 { 01120 if (!cm) return NULL; 01121 return GET_PRIVATE(cm)->quote_tz; 01122 } 01123 01124 /******************************************************************** 01125 * gnc_commodity_set_mnemonic 01126 ********************************************************************/ 01127 01128 void 01129 gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic) 01130 { 01131 CommodityPrivate* priv; 01132 01133 if (!cm) return; 01134 priv = GET_PRIVATE(cm); 01135 if (priv->mnemonic == mnemonic) return; 01136 01137 gnc_commodity_begin_edit(cm); 01138 CACHE_REMOVE (priv->mnemonic); 01139 priv->mnemonic = CACHE_INSERT(mnemonic); 01140 01141 mark_commodity_dirty (cm); 01142 reset_printname(priv); 01143 reset_unique_name(priv); 01144 gnc_commodity_commit_edit(cm); 01145 } 01146 01147 /******************************************************************** 01148 * gnc_commodity_set_namespace 01149 ********************************************************************/ 01150 01151 void 01152 gnc_commodity_set_namespace(gnc_commodity * cm, const char * namespace) 01153 { 01154 QofBook *book; 01155 gnc_commodity_table *table; 01156 gnc_commodity_namespace *nsp; 01157 CommodityPrivate* priv; 01158 01159 if (!cm) return; 01160 priv = GET_PRIVATE(cm); 01161 book = qof_instance_get_book (&cm->inst); 01162 table = gnc_commodity_table_get_table(book); 01163 nsp = gnc_commodity_table_add_namespace(table, namespace, book); 01164 if (priv->namespace == nsp) 01165 return; 01166 01167 gnc_commodity_begin_edit(cm); 01168 priv->namespace = nsp; 01169 if (nsp->iso4217) 01170 priv->quote_source = gnc_quote_source_lookup_by_internal("currency"); 01171 mark_commodity_dirty(cm); 01172 reset_printname(priv); 01173 reset_unique_name(priv); 01174 gnc_commodity_commit_edit(cm); 01175 } 01176 01177 /******************************************************************** 01178 * gnc_commodity_set_fullname 01179 ********************************************************************/ 01180 01181 void 01182 gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname) 01183 { 01184 CommodityPrivate* priv; 01185 01186 if (!cm) return; 01187 priv = GET_PRIVATE(cm); 01188 if (priv->fullname == fullname) return; 01189 01190 CACHE_REMOVE (priv->fullname); 01191 priv->fullname = CACHE_INSERT (fullname); 01192 01193 gnc_commodity_begin_edit(cm); 01194 mark_commodity_dirty(cm); 01195 reset_printname(priv); 01196 gnc_commodity_commit_edit(cm); 01197 } 01198 01199 /******************************************************************** 01200 * gnc_commodity_set_cusip 01201 ********************************************************************/ 01202 01203 void 01204 gnc_commodity_set_cusip(gnc_commodity * cm, 01205 const char * cusip) 01206 { 01207 CommodityPrivate* priv; 01208 01209 if (!cm) return; 01210 01211 priv = GET_PRIVATE(cm); 01212 if (priv->cusip == cusip) return; 01213 01214 gnc_commodity_begin_edit(cm); 01215 CACHE_REMOVE (priv->cusip); 01216 priv->cusip = CACHE_INSERT (cusip); 01217 mark_commodity_dirty(cm); 01218 gnc_commodity_commit_edit(cm); 01219 } 01220 01221 /******************************************************************** 01222 * gnc_commodity_set_fraction 01223 ********************************************************************/ 01224 01225 void 01226 gnc_commodity_set_fraction(gnc_commodity * cm, int fraction) 01227 { 01228 if (!cm) return; 01229 gnc_commodity_begin_edit(cm); 01230 GET_PRIVATE(cm)->fraction = fraction; 01231 mark_commodity_dirty(cm); 01232 gnc_commodity_commit_edit(cm); 01233 } 01234 01235 /******************************************************************** 01236 * gnc_commodity_set_auto_quote_control_flag 01237 ********************************************************************/ 01238 01239 static void 01240 gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm, 01241 const gboolean flag) 01242 { 01243 ENTER ("(cm=%p, flag=%d)", cm, flag); 01244 01245 if (!cm) 01246 { 01247 LEAVE(""); 01248 return; 01249 } 01250 01251 gnc_commodity_begin_edit(cm); 01252 kvp_frame_set_string(cm->inst.kvp_data, 01253 "auto_quote_control", flag ? NULL : "false"); 01254 mark_commodity_dirty(cm); 01255 gnc_commodity_commit_edit(cm); 01256 LEAVE(""); 01257 } 01258 01259 /******************************************************************** 01260 * gnc_commodity_user_set_quote_flag 01261 ********************************************************************/ 01262 01263 void 01264 gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag) 01265 { 01266 CommodityPrivate* priv; 01267 01268 ENTER ("(cm=%p, flag=%d)", cm, flag); 01269 01270 if (!cm) 01271 { 01272 LEAVE(""); 01273 return; 01274 } 01275 01276 priv = GET_PRIVATE(cm); 01277 gnc_commodity_begin_edit(cm); 01278 gnc_commodity_set_quote_flag(cm, flag); 01279 if (gnc_commodity_is_iso(cm)) 01280 { 01281 /* For currencies, disable auto quote control if the quote flag is being 01282 * changed from its default value and enable it if the quote flag is being 01283 * reset to its default value. The defaults for the quote flag are 01284 * disabled if no accounts are using the currency, and true otherwise. 01285 * Thus enable auto quote control if flag is FALSE and there are not any 01286 * accounts using this currency OR flag is TRUE and there are accounts 01287 * using this currency; otherwise disable auto quote control */ 01288 gnc_commodity_set_auto_quote_control_flag(cm, 01289 (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0))); 01290 } 01291 gnc_commodity_commit_edit(cm); 01292 LEAVE(""); 01293 } 01294 01295 /******************************************************************** 01296 * gnc_commodity_set_quote_flag 01297 ********************************************************************/ 01298 01299 void 01300 gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag) 01301 { 01302 ENTER ("(cm=%p, flag=%d)", cm, flag); 01303 01304 if (!cm) return; 01305 gnc_commodity_begin_edit(cm); 01306 GET_PRIVATE(cm)->quote_flag = flag; 01307 mark_commodity_dirty(cm); 01308 gnc_commodity_commit_edit(cm); 01309 LEAVE(" "); 01310 } 01311 01312 /******************************************************************** 01313 * gnc_commodity_set_quote_source 01314 ********************************************************************/ 01315 01316 void 01317 gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src) 01318 { 01319 ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->internal_name : "unknown"); 01320 01321 if (!cm) return; 01322 gnc_commodity_begin_edit(cm); 01323 GET_PRIVATE(cm)->quote_source = src; 01324 mark_commodity_dirty(cm); 01325 gnc_commodity_commit_edit(cm); 01326 LEAVE(" "); 01327 } 01328 01329 /******************************************************************** 01330 * gnc_commodity_set_quote_tz 01331 ********************************************************************/ 01332 01333 void 01334 gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz) 01335 { 01336 CommodityPrivate* priv; 01337 01338 if (!cm) return; 01339 01340 ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)"); 01341 01342 priv = GET_PRIVATE(cm); 01343 01344 if (tz == priv->quote_tz) 01345 { 01346 LEAVE("Already correct TZ"); 01347 return; 01348 } 01349 01350 gnc_commodity_begin_edit(cm); 01351 CACHE_REMOVE (priv->quote_tz); 01352 priv->quote_tz = CACHE_INSERT (tz); 01353 mark_commodity_dirty(cm); 01354 gnc_commodity_commit_edit(cm); 01355 LEAVE(" "); 01356 } 01357 01358 /******************************************************************** 01359 * gnc_commodity_increment_usage_count 01360 ********************************************************************/ 01361 01362 void 01363 gnc_commodity_increment_usage_count(gnc_commodity *cm) 01364 { 01365 CommodityPrivate* priv; 01366 01367 ENTER("(cm=%p)", cm); 01368 01369 if (!cm) 01370 { 01371 LEAVE(""); 01372 return; 01373 } 01374 01375 priv = GET_PRIVATE(cm); 01376 01377 if ((priv->usage_count == 0) && !priv->quote_flag 01378 && gnc_commodity_get_auto_quote_control_flag(cm) 01379 && gnc_commodity_is_iso(cm)) 01380 { 01381 /* compatability hack - Gnucash 1.8 gets currency quotes when a 01382 non-default currency is assigned to an account. */ 01383 gnc_commodity_begin_edit(cm); 01384 gnc_commodity_set_quote_flag(cm, TRUE); 01385 gnc_commodity_set_quote_source(cm, 01386 gnc_commodity_get_default_quote_source(cm)); 01387 gnc_commodity_commit_edit(cm); 01388 } 01389 priv->usage_count++; 01390 LEAVE("(usage_count=%d)", priv->usage_count); 01391 } 01392 01393 /******************************************************************** 01394 * gnc_commodity_decrement_usage_count 01395 ********************************************************************/ 01396 01397 void 01398 gnc_commodity_decrement_usage_count(gnc_commodity *cm) 01399 { 01400 CommodityPrivate* priv; 01401 01402 ENTER("(cm=%p)", cm); 01403 01404 if (!cm) 01405 { 01406 LEAVE(""); 01407 return; 01408 } 01409 01410 priv = GET_PRIVATE(cm); 01411 01412 if (priv->usage_count == 0) 01413 { 01414 PWARN("usage_count already zero"); 01415 LEAVE(""); 01416 return; 01417 } 01418 01419 priv->usage_count--; 01420 if ((priv->usage_count == 0) && priv->quote_flag 01421 && gnc_commodity_get_auto_quote_control_flag(cm) 01422 && gnc_commodity_is_iso(cm)) 01423 { 01424 /* if this is a currency with auto quote control enabled and no more 01425 * accounts reference this currency, disable quote retrieval */ 01426 gnc_commodity_set_quote_flag(cm, FALSE); 01427 } 01428 LEAVE("(usage_count=%d)", priv->usage_count); 01429 } 01430 01431 /********************************************************************\ 01432 \********************************************************************/ 01433 01434 01435 /******************************************************************** 01436 * gnc_commodity_equiv 01437 * are two commodities the same? 01438 ********************************************************************/ 01439 01440 gboolean 01441 gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b) 01442 { 01443 CommodityPrivate* priv_a; 01444 CommodityPrivate* priv_b; 01445 01446 if (a == b) return TRUE; 01447 if (!a || !b) return FALSE; 01448 01449 priv_a = GET_PRIVATE(a); 01450 priv_b = GET_PRIVATE(b); 01451 if (priv_a->namespace != priv_b->namespace) return FALSE; 01452 if (safe_strcmp(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE; 01453 return TRUE; 01454 } 01455 01456 gboolean 01457 gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b) 01458 { 01459 CommodityPrivate* priv_a; 01460 CommodityPrivate* priv_b; 01461 gboolean same_book; 01462 01463 if (a == b) return TRUE; 01464 01465 if (!a || !b) 01466 { 01467 DEBUG ("one is NULL"); 01468 return FALSE; 01469 } 01470 01471 priv_a = GET_PRIVATE(a); 01472 priv_b = GET_PRIVATE(b); 01473 same_book = qof_instance_get_book(QOF_INSTANCE(a)) == qof_instance_get_book(QOF_INSTANCE(b)); 01474 01475 if ((same_book && priv_a->namespace != priv_b->namespace) 01476 || (!same_book && safe_strcmp( gnc_commodity_namespace_get_name(priv_a->namespace), 01477 gnc_commodity_namespace_get_name(priv_b->namespace)) != 0)) 01478 { 01479 DEBUG ("namespaces differ: %p(%s) vs %p(%s)", 01480 priv_a->namespace, gnc_commodity_namespace_get_name(priv_a->namespace), 01481 priv_b->namespace, gnc_commodity_namespace_get_name(priv_b->namespace)); 01482 return FALSE; 01483 } 01484 01485 if (safe_strcmp(priv_a->mnemonic, priv_b->mnemonic) != 0) 01486 { 01487 DEBUG ("mnemonics differ: %s vs %s", priv_a->mnemonic, priv_b->mnemonic); 01488 return FALSE; 01489 } 01490 01491 if (safe_strcmp(priv_a->fullname, priv_b->fullname) != 0) 01492 { 01493 DEBUG ("fullnames differ: %s vs %s", priv_a->fullname, priv_b->fullname); 01494 return FALSE; 01495 } 01496 01497 if (safe_strcmp(priv_a->cusip, priv_b->cusip) != 0) 01498 { 01499 DEBUG ("cusips differ: %s vs %s", priv_a->cusip, priv_b->cusip); 01500 return FALSE; 01501 } 01502 01503 if (priv_a->fraction != priv_b->fraction) 01504 { 01505 DEBUG ("fractions differ: %d vs %d", priv_a->fraction, priv_b->fraction); 01506 return FALSE; 01507 } 01508 01509 return TRUE; 01510 } 01511 01512 int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b) 01513 { 01514 if (gnc_commodity_equal(a, b)) 01515 { 01516 return 0; 01517 } 01518 else 01519 { 01520 return 1; 01521 } 01522 } 01523 01524 int gnc_commodity_compare_void(const void * a, const void * b) 01525 { 01526 return gnc_commodity_compare(a, b); 01527 } 01528 01529 /************************************************************ 01530 * Namespace functions * 01531 ************************************************************/ 01532 const char * 01533 gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns) 01534 { 01535 if (ns == NULL) 01536 return NULL; 01537 return ns->name; 01538 } 01539 01540 GList * 01541 gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *namespace) 01542 { 01543 if (!namespace) 01544 return NULL; 01545 01546 return namespace->cm_list; 01547 } 01548 01549 gboolean 01550 gnc_commodity_namespace_is_iso(const char *namespace) 01551 { 01552 return ((safe_strcmp(namespace, GNC_COMMODITY_NS_ISO) == 0) || 01553 (safe_strcmp(namespace, GNC_COMMODITY_NS_CURRENCY) == 0)); 01554 } 01555 01556 static const gchar * 01557 gnc_commodity_table_map_namespace(const char * namespace) 01558 { 01559 if (safe_strcmp(namespace, GNC_COMMODITY_NS_ISO) == 0) 01560 return GNC_COMMODITY_NS_CURRENCY; 01561 return namespace; 01562 } 01563 01564 /******************************************************************** 01565 * gnc_commodity_table_new 01566 * make a new commodity table 01567 ********************************************************************/ 01568 01569 gnc_commodity_table * 01570 gnc_commodity_table_new(void) 01571 { 01572 gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1); 01573 retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal); 01574 retval->ns_list = NULL; 01575 return retval; 01576 } 01577 01578 /******************************************************************** 01579 * book anchor functons 01580 ********************************************************************/ 01581 01582 gnc_commodity_table * 01583 gnc_commodity_table_get_table(QofBook *book) 01584 { 01585 if (!book) return NULL; 01586 return qof_book_get_data (book, GNC_COMMODITY_TABLE); 01587 } 01588 01589 gnc_commodity * 01590 gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book) 01591 { 01592 gnc_commodity *twin; 01593 const char * ucom; 01594 gnc_commodity_table * comtbl; 01595 01596 if (!from) return NULL; 01597 comtbl = gnc_commodity_table_get_table (book); 01598 if (!comtbl) return NULL; 01599 01600 ucom = gnc_commodity_get_unique_name (from); 01601 twin = gnc_commodity_table_lookup_unique (comtbl, ucom); 01602 if (!twin) 01603 { 01604 twin = gnc_commodity_clone (from, book); 01605 twin = gnc_commodity_table_insert (comtbl, twin); 01606 } 01607 return twin; 01608 } 01609 01610 /******************************************************************** 01611 * gnc_commodity_table_get_size 01612 * get the size of the commodity table 01613 ********************************************************************/ 01614 01615 static void 01616 count_coms(gpointer key, gpointer value, gpointer user_data) 01617 { 01618 GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table; 01619 guint *count = (guint*)user_data; 01620 01621 if (safe_strcmp((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0) 01622 { 01623 /* don't count default commodities */ 01624 return; 01625 } 01626 01627 if (!value) return; 01628 01629 *count += g_hash_table_size(tbl); 01630 } 01631 01632 guint 01633 gnc_commodity_table_get_size(const gnc_commodity_table* tbl) 01634 { 01635 guint count = 0; 01636 g_return_val_if_fail(tbl, 0); 01637 g_return_val_if_fail(tbl->ns_table, 0); 01638 01639 g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count); 01640 01641 return count; 01642 } 01643 01644 /******************************************************************** 01645 * gnc_commodity_table_lookup 01646 * locate a commodity by namespace and mnemonic. 01647 ********************************************************************/ 01648 01649 gnc_commodity * 01650 gnc_commodity_table_lookup(const gnc_commodity_table * table, 01651 const char * namespace, const char * mnemonic) 01652 { 01653 gnc_commodity_namespace * nsp = NULL; 01654 unsigned int i; 01655 01656 if (!table || !namespace || !mnemonic) return NULL; 01657 01658 nsp = gnc_commodity_table_find_namespace(table, namespace); 01659 01660 if (nsp) 01661 { 01662 /* 01663 * Backward compatability support for currencies that have 01664 * recently changed. 01665 */ 01666 if (nsp->iso4217) 01667 { 01668 for (i = 0; i < GNC_NEW_ISO_CODES; i++) 01669 { 01670 if (strcmp(mnemonic, gnc_new_iso_codes[i].old_code) == 0) 01671 { 01672 mnemonic = gnc_new_iso_codes[i].new_code; 01673 break; 01674 } 01675 } 01676 } 01677 return g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic); 01678 } 01679 else 01680 { 01681 return NULL; 01682 } 01683 } 01684 01685 /******************************************************************** 01686 * gnc_commodity_table_lookup 01687 * locate a commodity by unique name. 01688 ********************************************************************/ 01689 01690 gnc_commodity * 01691 gnc_commodity_table_lookup_unique(const gnc_commodity_table *table, 01692 const char * unique_name) 01693 { 01694 char *namespace; 01695 char *mnemonic; 01696 gnc_commodity *commodity; 01697 01698 if (!table || !unique_name) return NULL; 01699 01700 namespace = g_strdup (unique_name); 01701 mnemonic = strstr (namespace, "::"); 01702 if (!mnemonic) 01703 { 01704 g_free (namespace); 01705 return NULL; 01706 } 01707 01708 *mnemonic = '\0'; 01709 mnemonic += 2; 01710 01711 commodity = gnc_commodity_table_lookup (table, namespace, mnemonic); 01712 01713 g_free (namespace); 01714 01715 return commodity; 01716 } 01717 01718 /******************************************************************** 01719 * gnc_commodity_table_find_full 01720 * locate a commodity by namespace and printable name 01721 ********************************************************************/ 01722 01723 gnc_commodity * 01724 gnc_commodity_table_find_full(const gnc_commodity_table * table, 01725 const char * namespace, 01726 const char * fullname) 01727 { 01728 gnc_commodity * retval = NULL; 01729 GList * all; 01730 GList * iterator; 01731 01732 if (!fullname || (fullname[0] == '\0')) 01733 return NULL; 01734 01735 all = gnc_commodity_table_get_commodities(table, namespace); 01736 01737 for (iterator = all; iterator; iterator = iterator->next) 01738 { 01739 if (!strcmp(fullname, 01740 gnc_commodity_get_printname(iterator->data))) 01741 { 01742 retval = iterator->data; 01743 break; 01744 } 01745 } 01746 01747 g_list_free (all); 01748 01749 return retval; 01750 } 01751 01752 01753 /******************************************************************** 01754 * gnc_commodity_table_insert 01755 * add a commodity to the table. 01756 ********************************************************************/ 01757 01758 gnc_commodity * 01759 gnc_commodity_table_insert(gnc_commodity_table * table, 01760 gnc_commodity * comm) 01761 { 01762 gnc_commodity_namespace * nsp = NULL; 01763 gnc_commodity *c; 01764 const char *ns_name; 01765 CommodityPrivate* priv; 01766 QofBook *book; 01767 01768 if (!table) return NULL; 01769 if (!comm) return NULL; 01770 01771 priv = GET_PRIVATE(comm); 01772 01773 ENTER ("(table=%p, comm=%p) %s %s", table, comm, 01774 (priv->mnemonic == NULL ? "(null)" : priv->mnemonic), 01775 (priv->fullname == NULL ? "(null)" : priv->fullname)); 01776 ns_name = gnc_commodity_namespace_get_name(priv->namespace); 01777 c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic); 01778 01779 if (c) 01780 { 01781 if (c == comm) 01782 { 01783 LEAVE("already in table"); 01784 return c; 01785 } 01786 01787 /* Backward compatability support for currencies that have 01788 * recently changed. */ 01789 if (priv->namespace->iso4217) 01790 { 01791 guint i; 01792 for (i = 0; i < GNC_NEW_ISO_CODES; i++) 01793 { 01794 if (!priv->mnemonic 01795 || !strcmp(priv->mnemonic, gnc_new_iso_codes[i].old_code)) 01796 { 01797 gnc_commodity_set_mnemonic(comm, gnc_new_iso_codes[i].new_code); 01798 break; 01799 } 01800 } 01801 } 01802 01803 gnc_commodity_copy (c, comm); 01804 gnc_commodity_destroy (comm); 01805 LEAVE("found at %p", c); 01806 return c; 01807 } 01808 01809 book = qof_instance_get_book (&comm->inst); 01810 nsp = gnc_commodity_table_add_namespace(table, ns_name, book); 01811 01812 PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic, 01813 nsp->cm_table, nsp->name); 01814 g_hash_table_insert(nsp->cm_table, 01815 CACHE_INSERT(priv->mnemonic), 01816 (gpointer)comm); 01817 nsp->cm_list = g_list_append(nsp->cm_list, comm); 01818 01819 qof_event_gen (&comm->inst, QOF_EVENT_ADD, NULL); 01820 LEAVE ("(table=%p, comm=%p)", table, comm); 01821 return comm; 01822 } 01823 01824 /******************************************************************** 01825 * gnc_commodity_table_remove 01826 * remove a commodity from the table. 01827 ********************************************************************/ 01828 01829 void 01830 gnc_commodity_table_remove(gnc_commodity_table * table, 01831 gnc_commodity * comm) 01832 { 01833 gnc_commodity_namespace * nsp; 01834 gnc_commodity *c; 01835 CommodityPrivate* priv; 01836 const char *ns_name; 01837 01838 if (!table) return; 01839 if (!comm) return; 01840 01841 priv = GET_PRIVATE(comm); 01842 ns_name = gnc_commodity_namespace_get_name(priv->namespace); 01843 c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic); 01844 if (c != comm) return; 01845 01846 qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, NULL); 01847 01848 nsp = gnc_commodity_table_find_namespace(table, ns_name); 01849 if (!nsp) return; 01850 01851 nsp->cm_list = g_list_remove(nsp->cm_list, comm); 01852 g_hash_table_remove (nsp->cm_table, priv->mnemonic); 01853 /* XXX minor mem leak, should remove the key as well */ 01854 } 01855 01856 /******************************************************************** 01857 * gnc_commodity_table_has_namespace 01858 * see if the commodities namespace exists. May have zero commodities. 01859 ********************************************************************/ 01860 01861 int 01862 gnc_commodity_table_has_namespace(const gnc_commodity_table * table, 01863 const char * namespace) 01864 { 01865 gnc_commodity_namespace * nsp = NULL; 01866 01867 if (!table || !namespace) 01868 { 01869 return 0; 01870 } 01871 01872 nsp = gnc_commodity_table_find_namespace(table, namespace); 01873 if (nsp) 01874 { 01875 return 1; 01876 } 01877 else 01878 { 01879 return 0; 01880 } 01881 } 01882 01883 static void 01884 hash_keys_helper(gpointer key, gpointer value, gpointer data) 01885 { 01886 GList ** l = data; 01887 *l = g_list_prepend(*l, key); 01888 } 01889 01890 static GList * 01891 g_hash_table_keys(GHashTable * table) 01892 { 01893 GList * l = NULL; 01894 g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l); 01895 return l; 01896 } 01897 01898 static void 01899 hash_values_helper(gpointer key, gpointer value, gpointer data) 01900 { 01901 GList ** l = data; 01902 *l = g_list_prepend(*l, value); 01903 } 01904 01905 static GList * 01906 g_hash_table_values(GHashTable * table) 01907 { 01908 GList * l = NULL; 01909 g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l); 01910 return l; 01911 } 01912 01913 /******************************************************************** 01914 * gnc_commodity_table_get_namespaces 01915 * see if any commodities in the namespace exist 01916 ********************************************************************/ 01917 01918 GList * 01919 gnc_commodity_table_get_namespaces(const gnc_commodity_table * table) 01920 { 01921 if (!table) 01922 return NULL; 01923 01924 return g_hash_table_keys(table->ns_table); 01925 } 01926 01927 GList * 01928 gnc_commodity_table_get_namespaces_list(const gnc_commodity_table * table) 01929 { 01930 if (!table) 01931 return NULL; 01932 01933 return table->ns_list; 01934 } 01935 01936 /* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to 01937 GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is 01938 either of these, the net result is that the iso4217 bit is set only 01939 for GNC_COMMODITY_NS_CURRENCY. This means that gnc_commodity_is_iso is 01940 a subset of gnc_commodity_is_currency. Most callers seem to use 01941 gnc_commodity_is_iso. */ 01942 gboolean 01943 gnc_commodity_is_iso(const gnc_commodity * cm) 01944 { 01945 CommodityPrivate* priv; 01946 01947 if (!cm) return FALSE; 01948 01949 priv = GET_PRIVATE(cm); 01950 if ( !priv->namespace) return FALSE; 01951 return priv->namespace->iso4217; 01952 } 01953 01954 gboolean 01955 gnc_commodity_is_currency(const gnc_commodity *cm) 01956 { 01957 const char *ns_name; 01958 if (!cm) return FALSE; 01959 01960 ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->namespace); 01961 return (!safe_strcmp(ns_name, GNC_COMMODITY_NS_LEGACY) || 01962 !safe_strcmp(ns_name, GNC_COMMODITY_NS_CURRENCY)); 01963 } 01964 01965 /******************************************************************** 01966 * gnc_commodity_table_get_commodities 01967 * list commodities in a give namespace 01968 ********************************************************************/ 01969 01970 CommodityList * 01971 gnc_commodity_table_get_commodities(const gnc_commodity_table * table, 01972 const char * namespace) 01973 { 01974 gnc_commodity_namespace * ns = NULL; 01975 01976 if (!table) 01977 return NULL; 01978 01979 ns = gnc_commodity_table_find_namespace(table, namespace); 01980 if (!ns) 01981 return NULL; 01982 01983 return g_hash_table_values(ns->cm_table); 01984 } 01985 01986 /******************************************************************** 01987 * gnc_commodity_table_get_quotable_commodities 01988 * list commodities in a given namespace that get price quotes 01989 ********************************************************************/ 01990 01991 static void 01992 get_quotables_helper1(gpointer key, gpointer value, gpointer data) 01993 { 01994 gnc_commodity *comm = value; 01995 CommodityPrivate* priv = GET_PRIVATE(comm); 01996 GList ** l = data; 01997 01998 if (!priv->quote_flag || 01999 !priv->quote_source || !priv->quote_source->supported) 02000 return; 02001 *l = g_list_prepend(*l, value); 02002 } 02003 02004 static gboolean 02005 get_quotables_helper2 (gnc_commodity *comm, gpointer data) 02006 { 02007 GList ** l = data; 02008 CommodityPrivate* priv = GET_PRIVATE(comm); 02009 02010 if (!priv->quote_flag || 02011 !priv->quote_source || !priv->quote_source->supported) 02012 return TRUE; 02013 *l = g_list_prepend(*l, comm); 02014 return TRUE; 02015 } 02016 02017 CommodityList * 02018 gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table) 02019 { 02020 gnc_commodity_namespace * ns = NULL; 02021 const char *namespace; 02022 GList * nslist, * tmp; 02023 GList * l = NULL; 02024 regex_t pattern; 02025 const char *expression = gnc_main_get_namespace_regexp(); 02026 02027 ENTER("table=%p, expression=%s", table, expression); 02028 if (!table) 02029 return NULL; 02030 02031 if (expression && *expression) 02032 { 02033 if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0) 02034 { 02035 LEAVE("Cannot compile regex"); 02036 return NULL; 02037 } 02038 02039 nslist = gnc_commodity_table_get_namespaces(table); 02040 for (tmp = nslist; tmp; tmp = tmp->next) 02041 { 02042 namespace = tmp->data; 02043 if (regexec(&pattern, namespace, 0, NULL, 0) == 0) 02044 { 02045 DEBUG("Running list of %s commodities", namespace); 02046 ns = gnc_commodity_table_find_namespace(table, namespace); 02047 if (ns) 02048 { 02049 g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l); 02050 } 02051 } 02052 } 02053 g_list_free(nslist); 02054 regfree(&pattern); 02055 } 02056 else 02057 { 02058 gnc_commodity_table_foreach_commodity(table, get_quotables_helper2, 02059 (gpointer) &l); 02060 } 02061 LEAVE("list head %p", l); 02062 return l; 02063 } 02064 02065 /******************************************************************** 02066 * gnc_commodity_table_add_namespace 02067 * add an empty namespace if it does not exist 02068 ********************************************************************/ 02069 02070 /* GObject Initialization */ 02071 QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE); 02072 02073 static void 02074 gnc_commodity_namespace_init(gnc_commodity_namespace* ns) 02075 { 02076 } 02077 02078 static void 02079 gnc_commodity_namespace_dispose_real (GObject *nsp) 02080 { 02081 } 02082 02083 static void 02084 gnc_commodity_namespace_finalize_real(GObject* nsp) 02085 { 02086 } 02087 02088 gnc_commodity_namespace * 02089 gnc_commodity_table_add_namespace(gnc_commodity_table * table, 02090 const char * namespace, 02091 QofBook *book) 02092 { 02093 gnc_commodity_namespace * ns = NULL; 02094 02095 if (!table) return NULL; 02096 02097 namespace = gnc_commodity_table_map_namespace(namespace); 02098 ns = gnc_commodity_table_find_namespace(table, namespace); 02099 if (!ns) 02100 { 02101 ns = g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, NULL); 02102 ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal); 02103 ns->name = CACHE_INSERT((gpointer)namespace); 02104 ns->iso4217 = gnc_commodity_namespace_is_iso(namespace); 02105 qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book); 02106 qof_event_gen (&ns->inst, QOF_EVENT_CREATE, NULL); 02107 02108 g_hash_table_insert(table->ns_table, 02109 (gpointer) ns->name, 02110 (gpointer) ns); 02111 table->ns_list = g_list_append(table->ns_list, ns); 02112 qof_event_gen (&ns->inst, QOF_EVENT_ADD, NULL); 02113 } 02114 return ns; 02115 } 02116 02117 02118 gnc_commodity_namespace * 02119 gnc_commodity_table_find_namespace(const gnc_commodity_table * table, 02120 const char * namespace) 02121 { 02122 if (!table || !namespace) 02123 return NULL; 02124 02125 namespace = gnc_commodity_table_map_namespace(namespace); 02126 return g_hash_table_lookup(table->ns_table, (gpointer)namespace); 02127 } 02128 02129 02130 gnc_commodity * 02131 gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book) 02132 { 02133 QofCollection *col; 02134 if (!guid || !book) return NULL; 02135 col = qof_book_get_collection (book, GNC_ID_COMMODITY); 02136 return (gnc_commodity *) qof_collection_lookup_entity (col, guid); 02137 } 02138 02139 /******************************************************************** 02140 * gnc_commodity_table_delete_namespace 02141 * delete a namespace 02142 ********************************************************************/ 02143 02144 static int 02145 ns_helper(gpointer key, gpointer value, gpointer user_data) 02146 { 02147 gnc_commodity * c = value; 02148 gnc_commodity_destroy(c); 02149 CACHE_REMOVE(key); /* key is commodity mnemonic */ 02150 return TRUE; 02151 } 02152 02153 void 02154 gnc_commodity_table_delete_namespace(gnc_commodity_table * table, 02155 const char * namespace) 02156 { 02157 gnc_commodity_namespace * ns; 02158 02159 if (!table) return; 02160 02161 ns = gnc_commodity_table_find_namespace(table, namespace); 02162 if (!ns) 02163 return; 02164 02165 qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, NULL); 02166 g_hash_table_remove(table->ns_table, namespace); 02167 table->ns_list = g_list_remove(table->ns_list, ns); 02168 02169 g_list_free(ns->cm_list); 02170 ns->cm_list = NULL; 02171 02172 g_hash_table_foreach_remove(ns->cm_table, ns_helper, NULL); 02173 g_hash_table_destroy(ns->cm_table); 02174 CACHE_REMOVE(ns->name); 02175 02176 qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, NULL); 02177 /* qof_instance_release(&ns->inst); */ 02178 g_object_unref(ns); 02179 } 02180 02181 /******************************************************************** 02182 * gnc_commodity_table_foreach_commodity 02183 * call user-defined function once for every commodity in every 02184 * namespace 02185 ********************************************************************/ 02186 02187 typedef struct 02188 { 02189 gboolean ok; 02190 gboolean (*func)(gnc_commodity *, gpointer); 02191 gpointer user_data; 02192 } IterData; 02193 02194 static void 02195 iter_commodity (gpointer key, gpointer value, gpointer user_data) 02196 { 02197 IterData *iter_data = (IterData *) user_data; 02198 gnc_commodity *cm = (gnc_commodity *) value; 02199 02200 if (iter_data->ok) 02201 { 02202 iter_data->ok = (iter_data->func)(cm, iter_data->user_data); 02203 } 02204 } 02205 02206 static void 02207 iter_namespace (gpointer key, gpointer value, gpointer user_data) 02208 { 02209 GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table; 02210 g_hash_table_foreach (namespace_hash, iter_commodity, user_data); 02211 } 02212 02213 gboolean 02214 gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl, 02215 gboolean (*f)(gnc_commodity *, gpointer), 02216 gpointer user_data) 02217 { 02218 IterData iter_data; 02219 02220 if (!tbl || !f) return FALSE; 02221 02222 iter_data.ok = TRUE; 02223 iter_data.func = f; 02224 iter_data.user_data = user_data; 02225 02226 g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data); 02227 02228 return iter_data.ok; 02229 } 02230 02231 /******************************************************************** 02232 * gnc_commodity_table_destroy 02233 * cleanup and free. 02234 ********************************************************************/ 02235 02236 void 02237 gnc_commodity_table_destroy(gnc_commodity_table * t) 02238 { 02239 gnc_commodity_namespace * ns; 02240 GList *item, *next; 02241 02242 if (!t) return; 02243 ENTER ("table=%p", t); 02244 02245 for (item = t->ns_list; item; item = next) 02246 { 02247 next = g_list_next(item); 02248 ns = item->data; 02249 gnc_commodity_table_delete_namespace(t, ns->name); 02250 } 02251 02252 g_list_free(t->ns_list); 02253 t->ns_list = NULL; 02254 g_hash_table_destroy(t->ns_table); 02255 t->ns_table = NULL; 02256 g_free(t); 02257 LEAVE ("table=%p", t); 02258 } 02259 02260 /* =========================================================== */ 02261 02262 /******************************************************************** 02263 * gnc_commodity_table_add_default_data 02264 ********************************************************************/ 02265 02266 #define CUR_I18N(String) dgettext ("iso_4217", String) 02267 02268 gboolean 02269 gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book) 02270 { 02271 QofCollection *col; 02272 gnc_commodity* c; 02273 02274 ENTER ("table=%p", table); 02275 gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_AMEX, book); 02276 gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_NYSE, book); 02277 gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_NASDAQ, book); 02278 gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_EUREX, book); 02279 gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_MUTUAL, book); 02280 gnc_commodity_table_add_namespace(table, "template", book); 02281 c = gnc_commodity_new(book, "template", "template", "template", "template", 1); 02282 gnc_commodity_table_insert(table, c); 02283 02284 #include "iso-4217-currencies.c" 02285 02286 /* We've just created the default namespaces and currencies. Mark 02287 * these collections as clean because there is no USER entered data 02288 * in these collections as of yet. */ 02289 col = qof_book_get_collection(book, GNC_ID_COMMODITY); 02290 qof_collection_mark_clean(col); 02291 col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE); 02292 qof_collection_mark_clean(col); 02293 02294 LEAVE ("table=%p", table); 02295 return TRUE; 02296 } 02297 02298 /******************************************************************** 02299 ********************************************************************/ 02300 /* QofObject function implementation and registration */ 02301 02302 #ifdef _MSC_VER 02303 /* MSVC compiler doesn't have C99 "designated initializers" 02304 * so we wrap them in a macro that is empty on MSVC. */ 02305 # define DI(x) /* */ 02306 #else 02307 # define DI(x) x 02308 #endif 02309 static QofObject commodity_object_def = 02310 { 02311 DI(.interface_version = ) QOF_OBJECT_VERSION, 02312 DI(.e_type = ) GNC_ID_COMMODITY, 02313 DI(.type_label = ) "Commodity", 02314 DI(.create = ) NULL, 02315 DI(.book_begin = ) NULL, 02316 DI(.book_end = ) NULL, 02317 DI(.is_dirty = ) qof_collection_is_dirty, 02318 DI(.mark_clean = ) qof_collection_mark_clean, 02319 DI(.foreach = ) qof_collection_foreach, 02320 DI(.printable = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname, 02321 }; 02322 02323 static QofObject namespace_object_def = 02324 { 02325 DI(.interface_version = ) QOF_OBJECT_VERSION, 02326 DI(.e_type = ) GNC_ID_COMMODITY_NAMESPACE, 02327 DI(.type_label = ) "Namespace", 02328 DI(.create = ) NULL, 02329 DI(.book_begin = ) NULL, 02330 DI(.book_end = ) NULL, 02331 DI(.is_dirty = ) NULL, 02332 DI(.mark_clean = ) NULL, 02333 DI(.foreach = ) NULL, 02334 DI(.printable = ) NULL, 02335 }; 02336 02337 static void 02338 commodity_table_book_begin (QofBook *book) 02339 { 02340 gnc_commodity_table *ct; 02341 ENTER ("book=%p", book); 02342 02343 if (gnc_commodity_table_get_table(book)) 02344 return; 02345 02346 ct = gnc_commodity_table_new (); 02347 qof_book_set_data (book, GNC_COMMODITY_TABLE, ct); 02348 02349 if (!gnc_commodity_table_add_default_data(ct, book)) 02350 { 02351 PWARN("unable to initialize book's commodity_table"); 02352 } 02353 02354 LEAVE ("book=%p", book); 02355 } 02356 02357 static void 02358 commodity_table_book_end (QofBook *book) 02359 { 02360 gnc_commodity_table *ct; 02361 02362 ct = gnc_commodity_table_get_table (book); 02363 qof_book_set_data (book, GNC_COMMODITY_TABLE, NULL); 02364 gnc_commodity_table_destroy (ct); 02365 } 02366 02367 static QofObject commodity_table_object_def = 02368 { 02369 DI(.interface_version = ) QOF_OBJECT_VERSION, 02370 DI(.e_type = ) GNC_ID_COMMODITY_TABLE, 02371 DI(.type_label = ) "CommodityTable", 02372 DI(.create = ) NULL, 02373 DI(.book_begin = ) commodity_table_book_begin, 02374 DI(.book_end = ) commodity_table_book_end, 02375 DI(.is_dirty = ) qof_collection_is_dirty, 02376 DI(.mark_clean = ) qof_collection_mark_clean, 02377 DI(.foreach = ) NULL, 02378 DI(.printable = ) NULL, 02379 DI(.version_cmp = ) NULL, 02380 }; 02381 02382 gboolean 02383 gnc_commodity_table_register (void) 02384 { 02385 gnc_quote_source_init_tables(); 02386 02387 if (!qof_object_register (&commodity_object_def)) 02388 return FALSE; 02389 if (!qof_object_register (&namespace_object_def)) 02390 return FALSE; 02391 return qof_object_register (&commodity_table_object_def); 02392 } 02393 02394 /* ******************************************************************* 02395 * gnc_monetary methods 02396 ********************************************************************/ 02397 02399 MonetaryList * 02400 gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon) 02401 { 02402 MonetaryList *l = list, *tmp; 02403 for (tmp = list; tmp; tmp = tmp->next) 02404 { 02405 gnc_monetary *list_mon = tmp->data; 02406 if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity)) 02407 { 02408 list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value, 02409 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); 02410 break; 02411 } 02412 } 02413 02414 /* See if we found an entry, and add one if not */ 02415 if (tmp == NULL) 02416 { 02417 gnc_monetary *new_mon = g_new0(gnc_monetary, 1); 02418 *new_mon = add_mon; 02419 l = g_list_prepend(l, new_mon); 02420 } 02421 02422 return l; 02423 } 02424 02427 MonetaryList * 02428 gnc_monetary_list_delete_zeros(MonetaryList *list) 02429 { 02430 MonetaryList *node, *next; 02431 for (node = list; node; node = next) 02432 { 02433 gnc_monetary *mon = node->data; 02434 next = node->next; 02435 if (gnc_numeric_zero_p(mon->value)) 02436 { 02437 g_free(mon); 02438 list = g_list_delete_link(list, node); 02439 } 02440 } 02441 return list; 02442 } 02443 02445 void 02446 gnc_monetary_list_free(MonetaryList *list) 02447 { 02448 MonetaryList *tmp; 02449 for (tmp = list; tmp; tmp = tmp->next) 02450 { 02451 g_free(tmp->data); 02452 } 02453 02454 g_list_free(list); 02455 } 02456 02457 /* ========================= END OF FILE ============================== */
1.7.4