GnuCash 2.4.99
gnc-commodity.c
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 &currency_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 &currency_quote_source;
00390     if (safe_strcmp(name, currency_quote_source.old_internal_name) == 0)
00391         return &currency_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 &currency_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 &currency_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 ============================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines