window-main-summarybar.c

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-ui-util.h"
00038 #include "window-main-summarybar.h"
00039 
00040 typedef struct {
00041   GtkWidget * hbox;
00042   GtkWidget * totals_combo;
00043   GtkListStore *datamodel;
00044   int       component_id;
00045   int       cnxn_id;
00046 } GNCMainSummary;
00047 
00048 #define WINDOW_SUMMARYBAR_CM_CLASS "summary-bar"
00049 
00050 #define GCONF_SECTION    "window/pages/account_tree/summary"
00051 #define KEY_GRAND_TOTAL  "grand_total"
00052 #define KEY_NON_CURRENCY "non_currency"
00053 
00065 typedef struct {
00066   gnc_commodity * currency;
00067   gnc_numeric assets;
00068   gnc_numeric profits;
00069   gint total_mode;
00070 } GNCCurrencyAcc;
00071 
00072 
00073 /* defines for total_mode in GNCCurrencyAcc and GNCCurrencyItem */
00074 #define TOTAL_SINGLE           0
00075 #define TOTAL_CURR_TOTAL       1
00076 #define TOTAL_NON_CURR_TOTAL   2
00077 #define TOTAL_GRAND_TOTAL      3
00078 
00079 
00081 typedef struct {
00082   gnc_commodity *default_currency;
00083   gboolean euro;
00084   gboolean grand_total;
00085   gboolean non_currency;
00086   time_t start_date;
00087   time_t end_date;
00088 } GNCSummarybarOptions;
00089 
00094 static GNCCurrencyAcc *
00095 gnc_ui_get_currency_accumulator(GList **list, gnc_commodity * currency, gint total_mode)
00096 {
00097   GList *current;
00098   GNCCurrencyAcc *found;
00099 
00100   for (current = g_list_first(*list); current; current = g_list_next(current)) {
00101     found = current->data;
00102     if ((gnc_commodity_equiv(currency, found->currency))
00103         && (found->total_mode == total_mode)) {
00104       return found;
00105     }
00106   }
00107 
00108   found = g_new0 (GNCCurrencyAcc, 1);
00109   found->currency = currency;
00110   found->assets = gnc_numeric_zero ();
00111   found->profits = gnc_numeric_zero ();
00112   found->total_mode = total_mode;
00113   *list = g_list_append (*list, found);
00114 
00115   return found;
00116 }
00117 
00121 static void
00122 gnc_ui_accounts_recurse (Account *parent, GList **currency_list,
00123                          GNCSummarybarOptions options)
00124 {
00125   gnc_numeric start_amount;
00126   gnc_numeric start_amount_default_currency;
00127   gnc_numeric end_amount;
00128   gnc_numeric end_amount_default_currency;
00129   GNCAccountType account_type;  
00130   gnc_commodity * account_currency;
00131   gnc_commodity * euro_commodity;
00132   GNCCurrencyAcc *currency_accum = NULL;
00133   GNCCurrencyAcc *euro_accum = NULL;
00134   GNCCurrencyAcc *grand_total_accum = NULL;
00135   GNCCurrencyAcc *non_curr_accum = NULL;
00136   GList *children, *node;
00137   gboolean non_currency = FALSE;
00138   Timespec end_timespec;
00139   Timespec start_timespec;
00140 
00141   if (parent == NULL) return;
00142 
00143   children = gnc_account_get_children(parent);
00144   for (node = children; node; node = g_list_next(node))
00145   {
00146     Account *account = node->data;
00147 
00148     account_type = xaccAccountGetType(account);
00149     account_currency = xaccAccountGetCommodity(account);
00150 
00151     if(options.grand_total)
00152       grand_total_accum = gnc_ui_get_currency_accumulator(currency_list,
00153                                                           options.default_currency,
00154                                                           TOTAL_GRAND_TOTAL);
00155 
00156     if (options.euro)
00157     {
00158       euro_commodity = gnc_get_euro ();
00159       euro_accum = gnc_ui_get_currency_accumulator(currency_list,
00160                                                    euro_commodity,
00161                                                    TOTAL_CURR_TOTAL);
00162     }
00163     else
00164       euro_commodity = NULL;
00165 
00166     if(!gnc_commodity_is_currency(account_currency)) {
00167       non_currency = TRUE;
00168       non_curr_accum = gnc_ui_get_currency_accumulator(currency_list,
00169                                                        options.default_currency,
00170                                                        TOTAL_NON_CURR_TOTAL);
00171     }
00172 
00173     if(!non_currency || options.non_currency) {
00174       currency_accum = gnc_ui_get_currency_accumulator(currency_list,
00175                                                        account_currency,
00176                                                        TOTAL_SINGLE);
00177     }
00178 
00179     switch (account_type)
00180     {
00181       case ACCT_TYPE_BANK:
00182       case ACCT_TYPE_CASH:
00183       case ACCT_TYPE_ASSET:
00184       case ACCT_TYPE_STOCK:
00185       case ACCT_TYPE_MUTUAL:
00186       case ACCT_TYPE_CREDIT:
00187       case ACCT_TYPE_LIABILITY:
00188       case ACCT_TYPE_PAYABLE:
00189       case ACCT_TYPE_RECEIVABLE:
00190         end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date);
00191         timespecFromTime_t(&end_timespec, options.end_date);
00192         end_amount_default_currency = 
00193             xaccAccountConvertBalanceToCurrencyAsOfDate
00194             (account, end_amount, account_currency, options.default_currency,
00195              timespecToTime_t(timespecCanonicalDayTime(end_timespec)));
00196 
00197         if(!non_currency || options.non_currency) {
00198           currency_accum->assets =
00199             gnc_numeric_add (currency_accum->assets, end_amount,
00200                              gnc_commodity_get_fraction (account_currency),
00201                              GNC_RND_ROUND);
00202         }
00203 
00204         if(non_currency) {
00205           non_curr_accum->assets =
00206             gnc_numeric_add (non_curr_accum->assets, end_amount_default_currency,
00207                              gnc_commodity_get_fraction (options.default_currency),
00208                              GNC_RND_ROUND);
00209         }
00210 
00211         if(options.grand_total) {
00212           grand_total_accum->assets =
00213             gnc_numeric_add (grand_total_accum->assets, end_amount_default_currency,
00214                              gnc_commodity_get_fraction (options.default_currency),
00215                              GNC_RND_ROUND);
00216         }
00217 
00218         if (options.euro && (currency_accum != euro_accum)) {
00219           euro_accum->assets =
00220             gnc_numeric_add (euro_accum->assets,
00221                              gnc_convert_to_euro(account_currency, end_amount),
00222                              gnc_commodity_get_fraction (euro_commodity),
00223                              GNC_RND_ROUND);
00224         }
00225 
00226         gnc_ui_accounts_recurse(account, currency_list, options);
00227         break;
00228       case ACCT_TYPE_INCOME:
00229       case ACCT_TYPE_EXPENSE:
00230         start_amount = xaccAccountGetBalanceAsOfDate(account, options.start_date);
00231         timespecFromTime_t(&start_timespec, options.start_date);
00232         start_amount_default_currency = 
00233             xaccAccountConvertBalanceToCurrencyAsOfDate
00234             (account, start_amount, account_currency, options.default_currency,
00235              timespecToTime_t(timespecCanonicalDayTime(start_timespec)));
00236         end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date);
00237         timespecFromTime_t(&end_timespec, options.end_date);
00238         end_amount_default_currency = 
00239             xaccAccountConvertBalanceToCurrencyAsOfDate
00240             (account, end_amount, account_currency, options.default_currency,
00241              timespecToTime_t(timespecCanonicalDayTime(end_timespec)));
00242 
00243         if(!non_currency || options.non_currency) {
00244           currency_accum->profits =
00245             gnc_numeric_add (currency_accum->profits, start_amount,
00246                              gnc_commodity_get_fraction (account_currency),
00247                              GNC_RND_ROUND);
00248           currency_accum->profits =
00249             gnc_numeric_sub (currency_accum->profits, end_amount,
00250                              gnc_commodity_get_fraction (account_currency),
00251                              GNC_RND_ROUND);
00252         }
00253 
00254         if(non_currency) {
00255           non_curr_accum->profits =
00256             gnc_numeric_add (non_curr_accum->profits, start_amount_default_currency,
00257                              gnc_commodity_get_fraction (options.default_currency),
00258                              GNC_RND_ROUND);
00259           non_curr_accum->profits =
00260             gnc_numeric_sub (non_curr_accum->profits, end_amount_default_currency,
00261                              gnc_commodity_get_fraction (options.default_currency),
00262                              GNC_RND_ROUND);
00263         }
00264 
00265         if(options.grand_total) {
00266           grand_total_accum->profits =
00267             gnc_numeric_add (grand_total_accum->profits,
00268                              start_amount_default_currency,
00269                              gnc_commodity_get_fraction (options.default_currency),
00270                              GNC_RND_ROUND);
00271           grand_total_accum->profits =
00272             gnc_numeric_sub (grand_total_accum->profits,
00273                              end_amount_default_currency,
00274                              gnc_commodity_get_fraction (options.default_currency),
00275                              GNC_RND_ROUND);
00276         }
00277 
00278         if (options.euro && (currency_accum != euro_accum)) {
00279           euro_accum->profits =
00280             gnc_numeric_add (euro_accum->profits,
00281                              gnc_convert_to_euro(account_currency, start_amount),
00282                              gnc_commodity_get_fraction (euro_commodity),
00283                              GNC_RND_ROUND);
00284           euro_accum->profits =
00285             gnc_numeric_sub (euro_accum->profits,
00286                              gnc_convert_to_euro(account_currency, end_amount),
00287                              gnc_commodity_get_fraction (euro_commodity),
00288                              GNC_RND_ROUND);
00289         }
00290 
00291         gnc_ui_accounts_recurse(account, currency_list, options);
00292         break;
00293       case ACCT_TYPE_EQUITY:
00294         /* no-op, see comments at top about summing assets */
00295         break;
00296       case ACCT_TYPE_CURRENCY:
00297       default:
00298         break;
00299     }
00300   }
00301   g_list_free(children);
00302 }
00303 
00304 static char*
00305 get_total_mode_label(const char *mnemonic, int total_mode)
00306 {
00307   char *label_str;
00308   // i.e., "$, grand total," [profits: $12,345.67, assets: $23,456.78]
00309   switch(total_mode)
00310   {
00311   case TOTAL_CURR_TOTAL:
00312     label_str = g_strdup_printf( _("%s, Total:"), mnemonic );
00313     break;
00314   case TOTAL_NON_CURR_TOTAL:
00315     label_str = g_strdup_printf( _("%s, Non Currency Commodities Total:"), mnemonic );
00316     break;
00317   case TOTAL_GRAND_TOTAL:
00318     label_str = g_strdup_printf( _("%s, Grand Total:"), mnemonic );
00319     break;
00320   case TOTAL_SINGLE:
00321   default:
00322     label_str = g_strdup_printf( _("%s:"), mnemonic );
00323     break;
00324   }
00325   return label_str;
00326 }
00327 
00328 enum {
00329   COLUMN_MNEMONIC_TYPE,
00330   COLUMN_ASSETS,
00331   COLUMN_ASSETS_VALUE,
00332   COLUMN_PROFITS,
00333   COLUMN_PROFITS_VALUE,
00334   N_COLUMNS,
00335 };
00336 
00337 /* The gnc_main_window_summary_refresh() subroutine redraws summary
00338  * information. The statusbar includes two fields, titled 'profits'
00339  * and 'assets'. The total assets equal the sum of all of the
00340  * non-equity, non-income accounts.  In theory, assets also equals the
00341  * grand total value of the equity accounts, but that assumes that
00342  * folks are using the equity account type correctly (which is not
00343  * likely). Thus we show the sum of assets, rather than the sum of
00344  * equities.
00345  *
00346  * The EURO gets special treatment. There can be one line with
00347  * EUR amounts and a EUR (total) line which summs up all EURO
00348  * member currencies.
00349  *
00350  * There can be a 'grand total', too, which sums up all accounts
00351  * converted to one common currency and a total of all non
00352  * currency commodities (e.g. stock, funds).  */
00353 
00354 static void
00355 gnc_main_window_summary_refresh (GNCMainSummary * summary)
00356 {
00357   Account *root;
00358   char asset_string[256];
00359   char profit_string[256];
00360   GNCCurrencyAcc *currency_accum;
00361   GList *currency_list;
00362   GList *current;
00363   GNCSummarybarOptions options;
00364 
00365   options.default_currency = gnc_default_report_currency ();
00366 
00367   options.euro = gnc_gconf_get_bool(GCONF_GENERAL, KEY_ENABLE_EURO, NULL);
00368   options.grand_total =
00369     gnc_gconf_get_bool(GCONF_SECTION, KEY_GRAND_TOTAL, NULL);
00370   options.non_currency =
00371     gnc_gconf_get_bool(GCONF_SECTION, KEY_NON_CURRENCY, NULL);
00372   options.start_date = gnc_accounting_period_fiscal_start();
00373   options.end_date = gnc_accounting_period_fiscal_end();
00374 
00375   currency_list = NULL;
00376 
00377   /* grand total should be first in the list */
00378   if(options.grand_total) {
00379     gnc_ui_get_currency_accumulator (&currency_list, options.default_currency,
00380                                      TOTAL_GRAND_TOTAL);
00381   }
00382   /* Make sure there's at least one accumulator in the list. */
00383   gnc_ui_get_currency_accumulator (&currency_list, options.default_currency,
00384                                    TOTAL_SINGLE);
00385 
00386   root = gnc_get_current_root_account ();
00387   gnc_ui_accounts_recurse(root, &currency_list, options);
00388 
00389   {
00390     GtkTreeIter iter;
00391     char asset_amount_string[256], profit_amount_string[256];
00392     struct lconv *lc;
00393 
00394     lc = gnc_localeconv();
00395 
00396     g_object_ref(summary->datamodel);
00397     gtk_combo_box_set_model(GTK_COMBO_BOX(summary->totals_combo), NULL);
00398     gtk_list_store_clear(summary->datamodel);
00399     for (current = g_list_first(currency_list); current; current = g_list_next(current)) {
00400       const char *mnemonic;
00401       gchar *total_mode_label;
00402 
00403       currency_accum = current->data;
00404 
00405       if (gnc_commodity_equiv (currency_accum->currency, gnc_locale_default_currency ()))
00406         mnemonic = lc->currency_symbol;
00407       else
00408         mnemonic = gnc_commodity_get_mnemonic (currency_accum->currency);
00409       
00410       if (mnemonic == NULL)
00411         mnemonic = "";
00412 
00413       *asset_string= '\0';
00414       xaccSPrintAmount(asset_amount_string,
00415                        currency_accum->assets,
00416                        gnc_commodity_print_info(currency_accum->currency, TRUE));
00417 
00418       *profit_string= '\0';
00419       xaccSPrintAmount(profit_amount_string,
00420                        currency_accum->profits,
00421                        gnc_commodity_print_info(currency_accum->currency, TRUE));
00422 
00423       gtk_list_store_append(summary->datamodel, &iter);
00424       total_mode_label = get_total_mode_label(mnemonic, currency_accum->total_mode);
00425       gtk_list_store_set(summary->datamodel, &iter,
00426                          COLUMN_MNEMONIC_TYPE, total_mode_label,
00427                          COLUMN_ASSETS,        _("Assets:"),
00428                          COLUMN_ASSETS_VALUE,  asset_amount_string,
00429                          COLUMN_PROFITS,       _("Profits:"),
00430                          COLUMN_PROFITS_VALUE, profit_amount_string,
00431                          -1);
00432       g_free(total_mode_label);
00433     }
00434     gtk_combo_box_set_model(GTK_COMBO_BOX(summary->totals_combo),
00435                             GTK_TREE_MODEL(summary->datamodel));
00436     g_object_unref(summary->datamodel);
00437 
00438     gtk_combo_box_set_active(GTK_COMBO_BOX(summary->totals_combo), 0);
00439   }
00440 
00441   /* Free the list we created for this */
00442   for (current = g_list_first(currency_list);
00443        current;
00444        current = g_list_next(current)) {
00445     g_free(current->data);
00446   }
00447   g_list_free(currency_list);
00448 }
00449 
00450 static void
00451 gnc_main_window_summary_destroy_cb(GNCMainSummary *summary, gpointer data)
00452 {
00453   gnc_gconf_remove_anon_notification(GCONF_SECTION, summary->cnxn_id);
00454   gnc_unregister_gui_component(summary->component_id);
00455   g_free(summary);
00456 }
00457 
00458 static void
00459 summarybar_refresh_handler(GHashTable * changes, gpointer user_data)
00460 {
00461   GNCMainSummary * summary = user_data;
00462   gnc_main_window_summary_refresh(summary);
00463 }
00464 
00465 static void
00466 gconf_client_notify_cb (GConfClient *client,
00467                         guint cnxn_id,
00468                         GConfEntry *entry,
00469                         gpointer user_data)
00470 {
00471   GNCMainSummary * summary = user_data;
00472   gnc_main_window_summary_refresh(summary);
00473 }
00474 
00475 GtkWidget *
00476 gnc_main_window_summary_new (void)
00477 {
00478   GNCMainSummary  * retval = g_new0(GNCMainSummary, 1);
00479   GtkCellRenderer *textRenderer;
00480   int i;
00481   // These options lead to a better looking layout for the combo-box, where
00482   // the "Assets: $####.##" and "Profit: $####.##" values are visually next
00483   // to each other.
00484   gboolean expandOptions[] = { TRUE, FALSE, TRUE, FALSE, TRUE };
00485   
00486   retval->datamodel = gtk_list_store_new( N_COLUMNS,
00487                                           G_TYPE_STRING,
00488                                           G_TYPE_STRING,
00489                                           G_TYPE_STRING,
00490                                           G_TYPE_STRING,
00491                                           G_TYPE_STRING );
00492 
00493   retval->hbox         = gtk_hbox_new (FALSE, 5);
00494   retval->totals_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (retval->datamodel));
00495   g_object_unref (retval->datamodel);
00496 
00497   retval->component_id = gnc_register_gui_component (WINDOW_SUMMARYBAR_CM_CLASS,
00498                                                      summarybar_refresh_handler,
00499                                                      NULL, retval);
00500   gnc_gui_component_watch_entity_type (retval->component_id,
00501                                        GNC_ID_ACCOUNT,
00502                                        QOF_EVENT_DESTROY
00503                                        | GNC_EVENT_ITEM_CHANGED);
00504 
00505   for ( i = 0; i < N_COLUMNS; i++ )
00506   {
00507     textRenderer = GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
00508     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(retval->totals_combo), textRenderer, expandOptions[i] );
00509     gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(retval->totals_combo), textRenderer, "text", i );
00510   }
00511 
00512   gtk_container_set_border_width (GTK_CONTAINER (retval->hbox), 2);
00513   gtk_box_pack_start (GTK_BOX(retval->hbox), retval->totals_combo, TRUE, TRUE, 5);
00514   gtk_widget_show (retval->totals_combo);
00515   gtk_widget_show (retval->hbox);
00516 
00517   g_signal_connect_swapped (G_OBJECT (retval->hbox), "destroy",
00518                             G_CALLBACK (gnc_main_window_summary_destroy_cb),
00519                             retval);
00520 
00521   gnc_main_window_summary_refresh(retval);
00522 
00523   retval->cnxn_id =  gnc_gconf_add_anon_notification(GCONF_SECTION,
00524                                                      gconf_client_notify_cb,
00525                                                      retval);
00526 
00527   return retval->hbox;
00528 }
00529 

Generated on Sun Sep 7 05:07:45 2008 for GnuCash by  doxygen 1.5.2