diff --git a/src/engine/gnc-pricedb.c b/src/engine/gnc-pricedb.c index 6d3c665fc0..bba4f28993 100644 --- a/src/engine/gnc-pricedb.c +++ b/src/engine/gnc-pricedb.c @@ -1407,11 +1407,21 @@ gnc_pricedb_remove_old_prices(GNCPriceDB *db, /* ==================================================================== */ /* lookup/query functions */ +static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b); + static void hash_values_helper(gpointer key, gpointer value, gpointer data) { GList ** l = data; - *l = g_list_concat(*l, g_list_copy (value)); + if (*l) + { + GList *new_l; + new_l = pricedb_price_list_merge(*l, value); + g_list_free (*l); + *l = new_l; + } + else + *l = g_list_copy (value); } static PriceList * @@ -1511,10 +1521,7 @@ pricedb_get_prices_internal(GNCPriceDB *db, const gnc_commodity *commodity, } } } - if (forward_list && !currency) - /* Multiple currencies, reverse_list doesn't exist and - forward_list must be sorted */ - forward_list = g_list_sort (forward_list, compare_prices_by_date); + return forward_list; } @@ -1933,7 +1940,124 @@ gnc_pricedb_get_prices(GNCPriceDB *db, return result; } +/* Return the number of prices in the data base for the given commodity + */ +static void +price_count_helper(gpointer key, gpointer value, gpointer data) +{ + int *result = data; + GList *price_list = value; + + *result += g_list_length(price_list); +} +int +gnc_pricedb_num_prices(GNCPriceDB *db, + const gnc_commodity *c) +{ + int result = 0; + GHashTable *currency_hash; + + if (!db || !c) return 0; + ENTER ("db=%p commodity=%p", db, c); + + currency_hash = g_hash_table_lookup(db->commodity_hash, c); + if (currency_hash) + { + g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result); + } + + LEAVE ("count=%d", result); + return result; +} + +/* Return the nth price for the given commodity + */ +GNCPrice * +gnc_pricedb_nth_price (GNCPriceDB *db, + const gnc_commodity *c, + const int n) +{ + GNCPrice *result = NULL; + GHashTable *currency_hash; + + if (!db || !c || n < 0) return NULL; + ENTER ("db=%p commodity=%p index=%d", db, c, n); + + currency_hash = g_hash_table_lookup(db->commodity_hash, c); + if (currency_hash) + { + int num_currencies = g_hash_table_size(currency_hash); + if (num_currencies == 1) + { + /* Optimize the case of prices in only one currency, it's common + and much faster */ + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, currency_hash); + if (g_hash_table_iter_next(&iter, &key, &value)) + { + PriceList *prices = value; + result = g_list_nth_data(prices, n); + } + } + else if (num_currencies > 1) + { + /* Prices for multiple currencies, must find the nth entry in the + merged currency list. */ + GList **price_array = (GList **)g_new(gpointer, num_currencies); + GList **next_list; + int i, j; + GHashTableIter iter; + gpointer key, value; + + /* Build an array of all the currencies this commodity has prices for */ + for (i = 0, g_hash_table_iter_init(&iter, currency_hash); + g_hash_table_iter_next(&iter, &key, &value) && i < num_currencies; + i++) + { + price_array[i] = value; + } + + /* Iterate n times to get the nth price, each time finding the currency + with the latest price */ + for (i = 0; i <= n; i++) + { + next_list = NULL; + for (j = 0; j < num_currencies; j++) + { + /* Save this entry if it's the first one or later than + the saved one. */ + if (price_array[j] != NULL && + (next_list == NULL || *next_list == NULL || + compare_prices_by_date((*next_list)->data, (price_array[j])->data) > 0)) + { + next_list = &price_array[j]; + } + } + /* next_list points to the list with the latest price unless all + the lists are empty */ + if (next_list && *next_list) + { + result = (*next_list)->data; + *next_list = (*next_list)->next; + } + else + { + /* all the lists are empty, "n" is greater than the number + of prices for this commodity. */ + result = NULL; + break; + } + } + g_free(price_array); + } + } + + LEAVE ("price=%p", result); + return result; +} + GNCPrice * gnc_pricedb_lookup_day(GNCPriceDB *db, const gnc_commodity *c, diff --git a/src/engine/gnc-pricedb.h b/src/engine/gnc-pricedb.h index 066cc8191f..4e3363f3a3 100644 --- a/src/engine/gnc-pricedb.h +++ b/src/engine/gnc-pricedb.h @@ -587,6 +587,27 @@ gboolean gnc_pricedb_foreach_price(GNCPriceDB *db, gpointer user_data, gboolean stable_order); +/** @brief Get the number of prices, in any currency, for a given commodity. + * @param db The pricedb + * @param c The commodity + * @return The number of prices in the database for this commody, zero if none + */ +int +gnc_pricedb_num_prices(GNCPriceDB *db, + const gnc_commodity *c); + +/** @brief Get the nth price for the given commodity in reverse date order + * @param db The pricedb + * @param c The commodity whose nth price is needed + * @param n Zero based index of the price wanted + * @return The nth price for this commodity in reverse chronological order, without + * regard for what currency the price is in + */ +GNCPrice * +gnc_pricedb_nth_price (GNCPriceDB *db, + const gnc_commodity *c, + const int n); + /* The following two convenience functions are used to test the xml backend */ /** @brief Return the number of prices in the database. * diff --git a/src/gnome-utils/gnc-tree-model-price.c b/src/gnome-utils/gnc-tree-model-price.c index 20a4409022..ec41172310 100644 --- a/src/gnome-utils/gnc-tree-model-price.c +++ b/src/gnome-utils/gnc-tree-model-price.c @@ -594,10 +594,8 @@ gnc_tree_model_price_get_iter (GtkTreeModel *tree_model, } /* Verify the third part of the path: the price. */ - price_list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL); i = gtk_tree_path_get_indices (path)[2]; - price = g_list_nth_data (price_list, i); - gnc_price_list_destroy(price_list); + price = gnc_pricedb_nth_price(priv->price_db, commodity, i); /* There's a race condition here that I can't resolve. * Comment this check out for now, and we'll handle the * resulting problem elsewhere. */ @@ -860,9 +858,7 @@ gnc_tree_model_price_iter_next (GtkTreeModel *tree_model, { commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2); n = GPOINTER_TO_INT(iter->user_data3) + 1; - list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL); - iter->user_data2 = g_list_nth_data(list, n); - gnc_price_list_destroy(list); + iter->user_data2 = gnc_pricedb_nth_price(priv->price_db, commodity, n); if (iter->user_data2 == NULL) { LEAVE("no next iter"); @@ -936,18 +932,18 @@ gnc_tree_model_price_iter_children (GtkTreeModel *tree_model, if (parent->user_data == ITER_IS_COMMODITY) { + GNCPrice *price; commodity = (gnc_commodity *)parent->user_data2; - list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL); - if (list == NULL) + price = gnc_pricedb_nth_price(priv->price_db, commodity, 0); + if (price == NULL) { LEAVE("no prices"); return FALSE; } iter->stamp = model->stamp; iter->user_data = ITER_IS_PRICE; - iter->user_data2 = g_list_nth_data(list, 0); + iter->user_data2 = price; iter->user_data3 = GINT_TO_POINTER(0); - gnc_price_list_destroy(list); LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter)); return TRUE; } @@ -1038,9 +1034,7 @@ gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model, if (iter->user_data == ITER_IS_COMMODITY) { commodity = (gnc_commodity *)iter->user_data2; - list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL); - n = g_list_length(list); - gnc_price_list_destroy(list); + n = gnc_pricedb_num_prices(priv->price_db, commodity); LEAVE("price list length %d", n); return n; } @@ -1099,13 +1093,11 @@ gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model, if (parent->user_data == ITER_IS_COMMODITY) { commodity = (gnc_commodity *)parent->user_data2; - list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL); iter->stamp = model->stamp; iter->user_data = ITER_IS_PRICE; - iter->user_data2 = g_list_nth_data(list, n); + iter->user_data2 = gnc_pricedb_nth_price(priv->price_db, commodity, n); iter->user_data3 = GINT_TO_POINTER(n); - gnc_price_list_destroy(list); LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter)); return iter->user_data2 != NULL; }