|
GnuCash 2.4.99
|
00001 /******************************************************************** 00002 * window-main-summarybar.c -- summary of financial info * 00003 * Copyright (C) 1998,1999 Jeremy Collins * 00004 * Copyright (C) 1998,1999,2000 Linas Vepstas * 00005 * Copyright (C) 2001 Bill Gribble * 00006 * Copyright (C) 2005 Joshua Sled <jsled@asynchronous.org> * 00007 * * 00008 * This program is free software; you can redistribute it and/or * 00009 * modify it under the terms of the GNU General Public License as * 00010 * published by the Free Software Foundation; either version 2 of * 00011 * the License, or (at your option) any later version. * 00012 * * 00013 * This program is distributed in the hope that it will be useful, * 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00016 * GNU General Public License for more details. * 00017 * * 00018 * You should have received a copy of the GNU General Public License* 00019 * along with this program; if not, contact: * 00020 * * 00021 * Free Software Foundation Voice: +1-617-542-5942 * 00022 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00023 * Boston, MA 02110-1301, USA gnu@gnu.org * 00024 ********************************************************************/ 00025 00026 #include "config.h" 00027 00028 #include <gtk/gtk.h> 00029 #include <glib/gi18n.h> 00030 00031 #include "Account.h" 00032 #include "gnc-accounting-period.h" 00033 #include "gnc-component-manager.h" 00034 #include "gnc-euro.h" 00035 #include "gnc-event.h" 00036 #include "gnc-gconf-utils.h" 00037 #include "gnc-locale-utils.h" 00038 #include "gnc-ui-util.h" 00039 #include "window-main-summarybar.h" 00040 00041 typedef struct 00042 { 00043 GtkWidget * hbox; 00044 GtkWidget * totals_combo; 00045 GtkListStore *datamodel; 00046 int component_id; 00047 int cnxn_id; 00048 } GNCMainSummary; 00049 00050 #define WINDOW_SUMMARYBAR_CM_CLASS "summary-bar" 00051 00052 #define GCONF_SECTION "window/pages/account_tree/summary" 00053 #define KEY_GRAND_TOTAL "grand_total" 00054 #define KEY_NON_CURRENCY "non_currency" 00055 00067 typedef struct 00068 { 00069 gnc_commodity * currency; 00070 gnc_numeric assets; 00071 gnc_numeric profits; 00072 gint total_mode; 00073 } GNCCurrencyAcc; 00074 00075 00076 /* defines for total_mode in GNCCurrencyAcc and GNCCurrencyItem */ 00077 #define TOTAL_SINGLE 0 00078 #define TOTAL_CURR_TOTAL 1 00079 #define TOTAL_NON_CURR_TOTAL 2 00080 #define TOTAL_GRAND_TOTAL 3 00081 00082 00084 typedef struct 00085 { 00086 gnc_commodity *default_currency; 00087 gboolean euro; 00088 gboolean grand_total; 00089 gboolean non_currency; 00090 time_t start_date; 00091 time_t end_date; 00092 } GNCSummarybarOptions; 00093 00098 static GNCCurrencyAcc * 00099 gnc_ui_get_currency_accumulator(GList **list, gnc_commodity * currency, gint total_mode) 00100 { 00101 GList *current; 00102 GNCCurrencyAcc *found; 00103 00104 for (current = g_list_first(*list); current; current = g_list_next(current)) 00105 { 00106 found = current->data; 00107 if ((gnc_commodity_equiv(currency, found->currency)) 00108 && (found->total_mode == total_mode)) 00109 { 00110 return found; 00111 } 00112 } 00113 00114 found = g_new0 (GNCCurrencyAcc, 1); 00115 found->currency = currency; 00116 found->assets = gnc_numeric_zero (); 00117 found->profits = gnc_numeric_zero (); 00118 found->total_mode = total_mode; 00119 *list = g_list_append (*list, found); 00120 00121 return found; 00122 } 00123 00127 static void 00128 gnc_ui_accounts_recurse (Account *parent, GList **currency_list, 00129 GNCSummarybarOptions options) 00130 { 00131 gnc_numeric start_amount; 00132 gnc_numeric start_amount_default_currency; 00133 gnc_numeric end_amount; 00134 gnc_numeric end_amount_default_currency; 00135 GNCAccountType account_type; 00136 gnc_commodity * account_currency; 00137 gnc_commodity * euro_commodity; 00138 GNCCurrencyAcc *currency_accum = NULL; 00139 GNCCurrencyAcc *euro_accum = NULL; 00140 GNCCurrencyAcc *grand_total_accum = NULL; 00141 GNCCurrencyAcc *non_curr_accum = NULL; 00142 GList *children, *node; 00143 gboolean non_currency = FALSE; 00144 Timespec end_timespec; 00145 Timespec start_timespec; 00146 00147 if (parent == NULL) return; 00148 00149 children = gnc_account_get_children(parent); 00150 for (node = children; node; node = g_list_next(node)) 00151 { 00152 Account *account = node->data; 00153 00154 account_type = xaccAccountGetType(account); 00155 account_currency = xaccAccountGetCommodity(account); 00156 00157 if (options.grand_total) 00158 grand_total_accum = gnc_ui_get_currency_accumulator(currency_list, 00159 options.default_currency, 00160 TOTAL_GRAND_TOTAL); 00161 00162 if (options.euro) 00163 { 00164 euro_commodity = gnc_get_euro (); 00165 euro_accum = gnc_ui_get_currency_accumulator(currency_list, 00166 euro_commodity, 00167 TOTAL_CURR_TOTAL); 00168 } 00169 else 00170 euro_commodity = NULL; 00171 00172 if (!gnc_commodity_is_currency(account_currency)) 00173 { 00174 non_currency = TRUE; 00175 non_curr_accum = gnc_ui_get_currency_accumulator(currency_list, 00176 options.default_currency, 00177 TOTAL_NON_CURR_TOTAL); 00178 } 00179 00180 if (!non_currency || options.non_currency) 00181 { 00182 currency_accum = gnc_ui_get_currency_accumulator(currency_list, 00183 account_currency, 00184 TOTAL_SINGLE); 00185 } 00186 00187 switch (account_type) 00188 { 00189 case ACCT_TYPE_BANK: 00190 case ACCT_TYPE_CASH: 00191 case ACCT_TYPE_ASSET: 00192 case ACCT_TYPE_STOCK: 00193 case ACCT_TYPE_MUTUAL: 00194 case ACCT_TYPE_CREDIT: 00195 case ACCT_TYPE_LIABILITY: 00196 case ACCT_TYPE_PAYABLE: 00197 case ACCT_TYPE_RECEIVABLE: 00198 end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date); 00199 timespecFromTime_t(&end_timespec, options.end_date); 00200 end_amount_default_currency = 00201 xaccAccountConvertBalanceToCurrencyAsOfDate 00202 (account, end_amount, account_currency, options.default_currency, 00203 timespecToTime_t(timespecCanonicalDayTime(end_timespec))); 00204 00205 if (!non_currency || options.non_currency) 00206 { 00207 currency_accum->assets = 00208 gnc_numeric_add (currency_accum->assets, end_amount, 00209 gnc_commodity_get_fraction (account_currency), 00210 GNC_HOW_RND_ROUND_HALF_UP); 00211 } 00212 00213 if (non_currency) 00214 { 00215 non_curr_accum->assets = 00216 gnc_numeric_add (non_curr_accum->assets, end_amount_default_currency, 00217 gnc_commodity_get_fraction (options.default_currency), 00218 GNC_HOW_RND_ROUND_HALF_UP); 00219 } 00220 00221 if (options.grand_total) 00222 { 00223 grand_total_accum->assets = 00224 gnc_numeric_add (grand_total_accum->assets, end_amount_default_currency, 00225 gnc_commodity_get_fraction (options.default_currency), 00226 GNC_HOW_RND_ROUND_HALF_UP); 00227 } 00228 00229 if (options.euro && (currency_accum != euro_accum)) 00230 { 00231 euro_accum->assets = 00232 gnc_numeric_add (euro_accum->assets, 00233 gnc_convert_to_euro(account_currency, end_amount), 00234 gnc_commodity_get_fraction (euro_commodity), 00235 GNC_HOW_RND_ROUND_HALF_UP); 00236 } 00237 00238 gnc_ui_accounts_recurse(account, currency_list, options); 00239 break; 00240 case ACCT_TYPE_INCOME: 00241 case ACCT_TYPE_EXPENSE: 00242 start_amount = xaccAccountGetBalanceAsOfDate(account, options.start_date); 00243 timespecFromTime_t(&start_timespec, options.start_date); 00244 start_amount_default_currency = 00245 xaccAccountConvertBalanceToCurrencyAsOfDate 00246 (account, start_amount, account_currency, options.default_currency, 00247 timespecToTime_t(timespecCanonicalDayTime(start_timespec))); 00248 end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date); 00249 timespecFromTime_t(&end_timespec, options.end_date); 00250 end_amount_default_currency = 00251 xaccAccountConvertBalanceToCurrencyAsOfDate 00252 (account, end_amount, account_currency, options.default_currency, 00253 timespecToTime_t(timespecCanonicalDayTime(end_timespec))); 00254 00255 if (!non_currency || options.non_currency) 00256 { 00257 currency_accum->profits = 00258 gnc_numeric_add (currency_accum->profits, start_amount, 00259 gnc_commodity_get_fraction (account_currency), 00260 GNC_HOW_RND_ROUND_HALF_UP); 00261 currency_accum->profits = 00262 gnc_numeric_sub (currency_accum->profits, end_amount, 00263 gnc_commodity_get_fraction (account_currency), 00264 GNC_HOW_RND_ROUND_HALF_UP); 00265 } 00266 00267 if (non_currency) 00268 { 00269 non_curr_accum->profits = 00270 gnc_numeric_add (non_curr_accum->profits, start_amount_default_currency, 00271 gnc_commodity_get_fraction (options.default_currency), 00272 GNC_HOW_RND_ROUND_HALF_UP); 00273 non_curr_accum->profits = 00274 gnc_numeric_sub (non_curr_accum->profits, end_amount_default_currency, 00275 gnc_commodity_get_fraction (options.default_currency), 00276 GNC_HOW_RND_ROUND_HALF_UP); 00277 } 00278 00279 if (options.grand_total) 00280 { 00281 grand_total_accum->profits = 00282 gnc_numeric_add (grand_total_accum->profits, 00283 start_amount_default_currency, 00284 gnc_commodity_get_fraction (options.default_currency), 00285 GNC_HOW_RND_ROUND_HALF_UP); 00286 grand_total_accum->profits = 00287 gnc_numeric_sub (grand_total_accum->profits, 00288 end_amount_default_currency, 00289 gnc_commodity_get_fraction (options.default_currency), 00290 GNC_HOW_RND_ROUND_HALF_UP); 00291 } 00292 00293 if (options.euro && (currency_accum != euro_accum)) 00294 { 00295 euro_accum->profits = 00296 gnc_numeric_add (euro_accum->profits, 00297 gnc_convert_to_euro(account_currency, start_amount), 00298 gnc_commodity_get_fraction (euro_commodity), 00299 GNC_HOW_RND_ROUND_HALF_UP); 00300 euro_accum->profits = 00301 gnc_numeric_sub (euro_accum->profits, 00302 gnc_convert_to_euro(account_currency, end_amount), 00303 gnc_commodity_get_fraction (euro_commodity), 00304 GNC_HOW_RND_ROUND_HALF_UP); 00305 } 00306 00307 gnc_ui_accounts_recurse(account, currency_list, options); 00308 break; 00309 case ACCT_TYPE_EQUITY: 00310 /* no-op, see comments at top about summing assets */ 00311 break; 00316 case ACCT_TYPE_TRADING: 00317 break; 00318 case ACCT_TYPE_CURRENCY: 00319 default: 00320 break; 00321 } 00322 } 00323 g_list_free(children); 00324 } 00325 00326 static char* 00327 get_total_mode_label(const char *mnemonic, int total_mode) 00328 { 00329 char *label_str; 00330 // i.e., "$, grand total," [profits: $12,345.67, assets: $23,456.78] 00331 switch (total_mode) 00332 { 00333 case TOTAL_CURR_TOTAL: 00334 label_str = g_strdup_printf( _("%s, Total:"), mnemonic ); 00335 break; 00336 case TOTAL_NON_CURR_TOTAL: 00337 label_str = g_strdup_printf( _("%s, Non Currency Commodities Total:"), mnemonic ); 00338 break; 00339 case TOTAL_GRAND_TOTAL: 00340 label_str = g_strdup_printf( _("%s, Grand Total:"), mnemonic ); 00341 break; 00342 case TOTAL_SINGLE: 00343 default: 00344 label_str = g_strdup_printf( _("%s:"), mnemonic ); 00345 break; 00346 } 00347 return label_str; 00348 } 00349 00350 enum 00351 { 00352 COLUMN_MNEMONIC_TYPE, 00353 COLUMN_ASSETS, 00354 COLUMN_ASSETS_VALUE, 00355 COLUMN_PROFITS, 00356 COLUMN_PROFITS_VALUE, 00357 N_COLUMNS, 00358 }; 00359 00360 /* The gnc_main_window_summary_refresh() subroutine redraws summary 00361 * information. The statusbar includes two fields, titled 'profits' 00362 * and 'assets'. The total assets equal the sum of all of the 00363 * non-equity, non-income accounts. In theory, assets also equals the 00364 * grand total value of the equity accounts, but that assumes that 00365 * folks are using the equity account type correctly (which is not 00366 * likely). Thus we show the sum of assets, rather than the sum of 00367 * equities. 00368 * 00369 * The EURO gets special treatment. There can be one line with 00370 * EUR amounts and a EUR (total) line which summs up all EURO 00371 * member currencies. 00372 * 00373 * There can be a 'grand total', too, which sums up all accounts 00374 * converted to one common currency and a total of all non 00375 * currency commodities (e.g. stock, funds). */ 00376 00377 static void 00378 gnc_main_window_summary_refresh (GNCMainSummary * summary) 00379 { 00380 Account *root; 00381 char asset_string[256]; 00382 char profit_string[256]; 00383 GNCCurrencyAcc *currency_accum; 00384 GList *currency_list; 00385 GList *current; 00386 GNCSummarybarOptions options; 00387 00388 00389 root = gnc_get_current_root_account (); 00390 options.default_currency = xaccAccountGetCommodity(root); 00391 if (options.default_currency == NULL) 00392 { 00393 options.default_currency = gnc_default_currency (); 00394 } 00395 00396 options.euro = gnc_gconf_get_bool(GCONF_GENERAL, KEY_ENABLE_EURO, NULL); 00397 options.grand_total = 00398 gnc_gconf_get_bool(GCONF_SECTION, KEY_GRAND_TOTAL, NULL); 00399 options.non_currency = 00400 gnc_gconf_get_bool(GCONF_SECTION, KEY_NON_CURRENCY, NULL); 00401 options.start_date = gnc_accounting_period_fiscal_start(); 00402 options.end_date = gnc_accounting_period_fiscal_end(); 00403 00404 currency_list = NULL; 00405 00406 /* grand total should be first in the list */ 00407 if (options.grand_total) 00408 { 00409 gnc_ui_get_currency_accumulator (¤cy_list, options.default_currency, 00410 TOTAL_GRAND_TOTAL); 00411 } 00412 /* Make sure there's at least one accumulator in the list. */ 00413 gnc_ui_get_currency_accumulator (¤cy_list, options.default_currency, 00414 TOTAL_SINGLE); 00415 00416 gnc_ui_accounts_recurse(root, ¤cy_list, options); 00417 00418 { 00419 GtkTreeIter iter; 00420 char asset_amount_string[256], profit_amount_string[256]; 00421 struct lconv *lc; 00422 00423 lc = gnc_localeconv(); 00424 00425 g_object_ref(summary->datamodel); 00426 gtk_combo_box_set_model(GTK_COMBO_BOX(summary->totals_combo), NULL); 00427 gtk_list_store_clear(summary->datamodel); 00428 for (current = g_list_first(currency_list); current; current = g_list_next(current)) 00429 { 00430 const char *mnemonic; 00431 gchar *total_mode_label; 00432 00433 currency_accum = current->data; 00434 00435 if (gnc_commodity_equiv (currency_accum->currency, gnc_locale_default_currency ())) 00436 mnemonic = lc->currency_symbol; 00437 else 00438 mnemonic = gnc_commodity_get_mnemonic (currency_accum->currency); 00439 00440 if (mnemonic == NULL) 00441 mnemonic = ""; 00442 00443 *asset_string = '\0'; 00444 xaccSPrintAmount(asset_amount_string, 00445 currency_accum->assets, 00446 gnc_commodity_print_info(currency_accum->currency, TRUE)); 00447 00448 *profit_string = '\0'; 00449 xaccSPrintAmount(profit_amount_string, 00450 currency_accum->profits, 00451 gnc_commodity_print_info(currency_accum->currency, TRUE)); 00452 00453 gtk_list_store_append(summary->datamodel, &iter); 00454 total_mode_label = get_total_mode_label(mnemonic, currency_accum->total_mode); 00455 gtk_list_store_set(summary->datamodel, &iter, 00456 COLUMN_MNEMONIC_TYPE, total_mode_label, 00457 COLUMN_ASSETS, _("Net Assets:"), 00458 COLUMN_ASSETS_VALUE, asset_amount_string, 00459 COLUMN_PROFITS, _("Profits:"), 00460 COLUMN_PROFITS_VALUE, profit_amount_string, 00461 -1); 00462 g_free(total_mode_label); 00463 } 00464 gtk_combo_box_set_model(GTK_COMBO_BOX(summary->totals_combo), 00465 GTK_TREE_MODEL(summary->datamodel)); 00466 g_object_unref(summary->datamodel); 00467 00468 gtk_combo_box_set_active(GTK_COMBO_BOX(summary->totals_combo), 0); 00469 } 00470 00471 /* Free the list we created for this */ 00472 for (current = g_list_first(currency_list); 00473 current; 00474 current = g_list_next(current)) 00475 { 00476 g_free(current->data); 00477 } 00478 g_list_free(currency_list); 00479 } 00480 00481 static void 00482 gnc_main_window_summary_destroy_cb(GNCMainSummary *summary, gpointer data) 00483 { 00484 gnc_gconf_remove_anon_notification(GCONF_SECTION, summary->cnxn_id); 00485 gnc_unregister_gui_component(summary->component_id); 00486 g_free(summary); 00487 } 00488 00489 static void 00490 summarybar_refresh_handler(GHashTable * changes, gpointer user_data) 00491 { 00492 GNCMainSummary * summary = user_data; 00493 gnc_main_window_summary_refresh(summary); 00494 } 00495 00496 static void 00497 gconf_client_notify_cb (GConfClient *client, 00498 guint cnxn_id, 00499 GConfEntry *entry, 00500 gpointer user_data) 00501 { 00502 GNCMainSummary * summary = user_data; 00503 gnc_main_window_summary_refresh(summary); 00504 } 00505 00506 GtkWidget * 00507 gnc_main_window_summary_new (void) 00508 { 00509 GNCMainSummary * retval = g_new0(GNCMainSummary, 1); 00510 GtkCellRenderer *textRenderer; 00511 int i; 00512 // These options lead to a better looking layout for the combo-box, where 00513 // the "Assets: $####.##" and "Profit: $####.##" values are visually next 00514 // to each other. 00515 gboolean expandOptions[] = { TRUE, FALSE, TRUE, FALSE, TRUE }; 00516 00517 retval->datamodel = gtk_list_store_new( N_COLUMNS, 00518 G_TYPE_STRING, 00519 G_TYPE_STRING, 00520 G_TYPE_STRING, 00521 G_TYPE_STRING, 00522 G_TYPE_STRING ); 00523 00524 retval->hbox = gtk_hbox_new (FALSE, 5); 00525 retval->totals_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (retval->datamodel)); 00526 g_object_unref (retval->datamodel); 00527 00528 retval->component_id = gnc_register_gui_component (WINDOW_SUMMARYBAR_CM_CLASS, 00529 summarybar_refresh_handler, 00530 NULL, retval); 00531 gnc_gui_component_watch_entity_type (retval->component_id, 00532 GNC_ID_ACCOUNT, 00533 QOF_EVENT_DESTROY 00534 | GNC_EVENT_ITEM_CHANGED); 00535 00536 for ( i = 0; i < N_COLUMNS; i++ ) 00537 { 00538 textRenderer = GTK_CELL_RENDERER(gtk_cell_renderer_text_new()); 00539 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(retval->totals_combo), textRenderer, expandOptions[i] ); 00540 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(retval->totals_combo), textRenderer, "text", i ); 00541 } 00542 00543 gtk_container_set_border_width (GTK_CONTAINER (retval->hbox), 2); 00544 gtk_box_pack_start (GTK_BOX(retval->hbox), retval->totals_combo, TRUE, TRUE, 5); 00545 gtk_widget_show (retval->totals_combo); 00546 gtk_widget_show (retval->hbox); 00547 00548 g_signal_connect_swapped (G_OBJECT (retval->hbox), "destroy", 00549 G_CALLBACK (gnc_main_window_summary_destroy_cb), 00550 retval); 00551 00552 gnc_main_window_summary_refresh(retval); 00553 00554 retval->cnxn_id = gnc_gconf_add_anon_notification(GCONF_SECTION, 00555 gconf_client_notify_cb, 00556 retval); 00557 00558 return retval->hbox; 00559 } 00560
1.7.4