GnuCash 2.4.99
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-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 (&currency_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 (&currency_list, options.default_currency,
00414                                      TOTAL_SINGLE);
00415 
00416     gnc_ui_accounts_recurse(root, &currency_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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines